You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(trustless-id): clarify client/verifier flow and add use-case doc 📝
- Add JSR badge to README
- Link USECASE.md in README documentation section
- Add USECASE.md with parties, flow, delivery channel, and secure vs leak cases
- Clarify client decodes and verifier only verifies in README and USAGE
- Clarify hashId is client-side; verifier does not need hashId
- Use backticks for API terms (connectorId, hashId, requestId, decode, verify) in README and USAGE
- Update session ID description (one-time or stable per user) in README
Use a **connector ID** (e.g. service URL or app identifier) on both sides. Generate a one-time **hashId**, create an **instance**, build a **requestId**, then decode to a numeric code or verify with a usersecret.
40
+
Use a **connector ID** (`connectorId`) (e.g. service URL or app identifier) on both sides. Generate a one-time `hashId` via `generate(connectorId)`, create an instance via `create(connectorId)`, build a `requestId` via `request(hashId, expireTime?)`, then **client** decodes to a numeric code (`codeId`) to show the user; **verifier** checks the user-entered code with `verify(requestId, secret)`.
|**Secret**| A shared secret is generated and stored by both the server and the user (e.g. in an authenticator app). | No stored secret. Only a connector ID (e.g. service URL or app identifier) is agreed on; the key is derived from it when needed. |
71
-
|**Code derivation**| Code is computed from the shared secret and the current time window (e.g. 30s) using HMAC-SHA1. | Code is derived from the session’s hashId and the encoded requestId using a FNV-1a–style mix; time is embedded in the request payload. |
72
-
|**Server / backend**| A server (or backend) must store the secret per user and validate the code on each login. | No server or database. Both sides use the same connector ID to derive the same key and verify the code locally. |
73
-
|**Typical use**| Two-factor authentication (2FA): prove that the user possesses the shared secret at login time. | One-time pairing or handshake: both sides agree on a connector; one creates a request, the other decodes and verifies the user-entered code within a short time window. |
|**Secret**| A shared secret is generated and stored by both the server and the user (e.g. in an authenticator app). | No stored secret. Only a connector ID (`connectorId`) (e.g. service URL or app identifier) is agreed on; the key is derived from it when needed.|
71
+
|**Code derivation**| Code is computed from the shared secret and the current time window (e.g. 30s) using HMAC-SHA1. | Code is derived from the session’s `hashId` and the encoded `requestId` using a FNV-1a–style mix; time is embedded in the request payload.|
72
+
|**Server / backend**| A server (or backend) must store the secret per user and validate the code on each login. | No server or database. Client uses same `connectorId` to derive key, decode to code; verifier uses same `connectorId` to verify the user-entered code locally.|
73
+
|**Typical use**| Two-factor authentication (2FA): prove that the user possesses the shared secret at login time. | One-time pairing or repeated identity: same connector; client uses `request` → `decode` → code, verifier uses `verify`. Use a new `hashId` per session (anonymous) or one stable `hashId` per user (repeated proof, no server storage). |
74
74
75
75
## Documentation
76
76
77
-
The full API, flow, and type reference are in **[USAGE.md](USAGE.md)**.
78
-
79
-
That document describes how to use the library end-to-end and lists every method and type.
77
+
-**[USAGE.md](USAGE.md)** — Full API, flow, and type reference; how to use the library end-to-end and every method and type.
78
+
-**[USECASE.md](USECASE.md)** — Use cases and scenarios to clarify flow and architecture (actors, channels, secure vs leak conditions).
// Request with 10s expiry window; send requestId to verifier (QR / link / form)
34
34
const requestId =instance.request(hashId, 10)
35
35
36
-
//Decode to code, or null if invalid/expired
36
+
//Client: decode to code and show to user
37
37
const codeId =instance.decode(hashId, requestId)
38
38
if (codeId!==null) {
39
-
//Verify user secret
39
+
//Verifier: check user-entered secret
40
40
const ok =instance.verify(requestId, codeId)
41
41
}
42
42
```
@@ -45,25 +45,25 @@ if (codeId !== null) {
45
45
46
46
1.**Connector** — Both client and verifier use the same `connectorId` (e.g. service URL).
47
47
2.**Hash** — Client calls `trustless.generate(connectorId)` once per session; returns a 197-char hex `hashId` (unique per call). This is the session identifier.
48
-
3.**Instance** — Both sides call `trustless.create(connectorId)` to get an instance bound to that connector.
49
-
4.**Request** — Client calls `instance.request(hashId, expireTime?)` to get `requestId`. Send `requestId`and `hashId`to the verifier (e.g. QR, link, form).
50
-
5.**Decode** — Verifier calls `instance.decode(hashId, requestId)` to get numeric `codeId`, or `null` if invalid/expired. Verifier must use the same `hashId` received from the client.
51
-
6.**Verify** — User enters the code (or it’s shown). Verifier calls `instance.verify(requestId, secret)`; returns `true` when not expired and code matches.
48
+
3.**Instance** — Both sides call `trustless.create(connectorId)` to get an instance (for `request`, `decode`, `verify`) bound to that connector.
49
+
4.**Request** — Client calls `instance.request(hashId, expireTime?)` to get `requestId`. Send `requestId` to the verifier (e.g. QR, link, form).
50
+
5.**Decode** — **Client** calls `instance.decode(hashId, requestId)` to get numeric `codeId`, or `null` if invalid/expired. Client shows the code to the user so they can type it at the verifier. The verifier does not call `decode`.
51
+
6.**Verify** — User enters the code at the verifier. **Verifier** calls `instance.verify(requestId, secret)` only; returns `true` when not expired and code matches.
|[`instance.request(hashId, expireTime?)`](#instancerequesthashid-expiretime)| instance |`RequestId`| Encoded payload string or `''` if `hashId` invalid.|
60
+
|[`instance.decode(hashId, requestId)`](#instancedecodehashid-requestid)| instance |`CodeId \| null`|**Client:** numeric code when valid (show to user).|
61
+
|[`instance.verify(requestId, secret)`](#instanceverifyrequestid-secret)| instance |`boolean`|**Verifier:** true when not expired and code matches.|
62
62
63
63
## ConnectorId and HashId
64
64
65
65
-**ConnectorId** — Any non-secret string that identifies the connector (e.g. `trustless://auth/example.com:0.1.0?service=none`). Trimmed before hashing. Same value on both sides yields the same encryption key.
66
-
-**HashId** — 197 lowercase hex chars from `trustless.generate(connectorId)`. Includes timestamp and random nonce; different on every call. Client must pass `hashId` together with `requestId` to the verifier so the verifier can call `decode(hashId, requestId)`.
66
+
-**HashId** — 197 lowercase hex chars from `trustless.generate(connectorId)`. Includes timestamp and random nonce; different on every call. Used by the **client** when calling `decode(hashId, requestId)` to obtain the code to show the user. The verifier does not need `hashId`; it only receives `requestId` and the user-entered code and calls `verify(requestId, secret)`.
67
67
68
68
```typescript
69
69
const hashId =trustless.generate(connectorId)
@@ -105,7 +105,7 @@ Create an instance bound to the given connector. The instance key is a hash of t
105
105
106
106
### trustless.generate(connectorId)
107
107
108
-
Generate a one-time 197-char hex hash. Uses connectorId + timestamp + random nonce. Call once per session; each call returns a different value.
108
+
Generate a one-time 197-char hex hash. Uses `connectorId` + timestamp + random nonce. Call once per session; each call returns a different value.
109
109
110
110
-`connectorId``<ConnectorId>`: Same identifier as used for `create`.
@@ -120,18 +120,18 @@ Build the encoded request payload for the given hash and optional expiry window.
120
120
121
121
### instance.decode(hashId, requestId)
122
122
123
-
Decode request payload and return the numeric code when hash matches and not expired.
123
+
**Client only.**Decode request payload and return the numeric code when hash matches and not expired. Use the returned code to show the user so they can enter it at the verifier.
124
124
125
125
-`hashId``<HashId>`: Expected hash for this session.
126
-
-`requestId``<RequestId>`: Encoded payload from the client.
126
+
-`requestId``<RequestId>`: Encoded payload from `request`.
127
127
- Returns: `<CodeId | null>` Integer in 0–1e10, or null when invalid/expired/wrong hash.
128
128
129
129
### instance.verify(requestId, secret)
130
130
131
-
Check that the user-provided secret matches the derived code and the payload is not expired.
131
+
**Verifier only.**Check that the user-provided secret matches the derived code and the payload is not expired.
132
132
133
-
-`requestId``<RequestId>`: Encoded payload.
134
-
-`secret``<VerifySecret>`: Number or string of digits (e.g. user input).
133
+
-`requestId``<RequestId>`: Encoded payload (received from client, e.g. via QR).
134
+
-`secret``<VerifySecret>`: Number or string of digits (user-entered code).
135
135
- Returns: `<boolean>` True when not expired and code matches.
0 commit comments