@ccmsg/protocol 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/common/auth.ts +44 -2
package/package.json
CHANGED
package/src/common/auth.ts
CHANGED
|
@@ -92,6 +92,13 @@ export const RegisterClaims = Type.Object(
|
|
|
92
92
|
expires_at: Timestamp,
|
|
93
93
|
/** Names this registration, so it can be spent once. */
|
|
94
94
|
jti: Type.String({ minLength: 1 }),
|
|
95
|
+
/** The WebAuthn user handle for this subject: sixteen random bytes the
|
|
96
|
+
* issuing instance settles on once per `sub`. The page creates the
|
|
97
|
+
* credential against it, the record keeps it, and an assertion that names a
|
|
98
|
+
* handle is held to it. It is here rather than left to the page because the
|
|
99
|
+
* authenticator stores it beyond this instance's reach — a second value for
|
|
100
|
+
* one person would be a second account on their device. */
|
|
101
|
+
user_id: Base64Url,
|
|
95
102
|
/** What the administrator who issued the URL wrote down about who it was
|
|
96
103
|
* for. Their words, not the holder's — the label the person gives their
|
|
97
104
|
* own device is `device_label` on the record, and the two are worth telling
|
|
@@ -242,7 +249,16 @@ export const AuthRefreshResponse = response("auth_refresh", AuthRefreshResult);
|
|
|
242
249
|
* verification, the record lookup — the receiving instance does itself. */
|
|
243
250
|
export const AuthResolveArgs = Type.Union(
|
|
244
251
|
[
|
|
245
|
-
Type.Object({
|
|
252
|
+
Type.Object({
|
|
253
|
+
kind: Type.Literal("register"),
|
|
254
|
+
token: Type.String({ minLength: 1 }),
|
|
255
|
+
/** The digits the person typed, forwarded unchecked. The issuer holds
|
|
256
|
+
* both the code and the count of attempts against it, so it is the only
|
|
257
|
+
* one that can refuse a wrong one and retire the URL after enough of
|
|
258
|
+
* them; a receiver that judged the code itself would let an attacker
|
|
259
|
+
* spread guesses across instances without any of them counting. */
|
|
260
|
+
code: Type.String({ pattern: "^[0-9]{6}$" }),
|
|
261
|
+
}),
|
|
246
262
|
Type.Object({ kind: Type.Literal("challenge"), challenge: Base64Url }),
|
|
247
263
|
],
|
|
248
264
|
{ $id: "AuthResolveArgs" },
|
|
@@ -300,7 +316,9 @@ export const CredentialRecord = Type.Object(
|
|
|
300
316
|
credential_id: Base64Url,
|
|
301
317
|
/** The public key, COSE-encoded. */
|
|
302
318
|
public_key: Base64Url,
|
|
303
|
-
/** The `
|
|
319
|
+
/** The `user_id` of the registration's claims, which is what the credential
|
|
320
|
+
* was created against and what an assertion naming a handle is checked
|
|
321
|
+
* against. */
|
|
304
322
|
user_handle: Base64Url,
|
|
305
323
|
/** The relying party this credential was created under, as the claims of
|
|
306
324
|
* the registration that made it stated. Written by the registration and not
|
|
@@ -355,6 +373,30 @@ export const TokenFamily = Type.Object(
|
|
|
355
373
|
refresh: Type.Object({ value: Base64Url, expires_at: Timestamp }),
|
|
356
374
|
/** The generation before the current one, while the grace for it lasts. */
|
|
357
375
|
previous_refresh: Type.Optional(Type.Object({ value: Base64Url, expires_at: Timestamp })),
|
|
376
|
+
/** What every generation retired before that was, kept only as a digest and
|
|
377
|
+
* only until the value itself would have expired.
|
|
378
|
+
*
|
|
379
|
+
* Recognising a replay takes remembering the value, but holding it is what
|
|
380
|
+
* the family is trying to protect — these travel to every instance, and a
|
|
381
|
+
* retired token still inside its lifetime would be a live secret copied
|
|
382
|
+
* around for no purpose it could serve. A digest answers the one question
|
|
383
|
+
* asked of it, that a value presented now was once issued here and is no
|
|
384
|
+
* longer, which fails the whole family.
|
|
385
|
+
*
|
|
386
|
+
* Written by the `iss` alone, like the rest of the family, and replicated,
|
|
387
|
+
* so the memory survives that instance restarting and holds wherever the
|
|
388
|
+
* reused value is presented. */
|
|
389
|
+
retired: Type.Optional(
|
|
390
|
+
Type.Array(
|
|
391
|
+
Type.Object({
|
|
392
|
+
/** sha256 of the retired value, lowercase hex. */
|
|
393
|
+
hash: Type.String({ pattern: "^[0-9a-f]{64}$" }),
|
|
394
|
+
/** When the value would have expired, after which remembering it
|
|
395
|
+
* refuses nothing that its own expiry would not. */
|
|
396
|
+
expires_at: Timestamp,
|
|
397
|
+
}),
|
|
398
|
+
),
|
|
399
|
+
),
|
|
358
400
|
},
|
|
359
401
|
{ $id: "TokenFamily" },
|
|
360
402
|
);
|