@ccmsg/protocol 2.4.0 → 2.5.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ccmsg/protocol",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "Wire contract (schema + types + op attribute table) shared by the ccmsg daemon and web UI",
5
5
  "license": "MIT",
6
6
  "author": "kawaz",
@@ -146,6 +146,43 @@ const ENROLL_CLAIMS_FIELDS = {
146
146
  * own device is `device_label` on the record, and the two are worth telling
147
147
  * apart when a list is read back later. */
148
148
  issued_label: Type.Optional(Type.String({ maxLength: 128 })),
149
+ /** What to call the account in the authenticator, as the administrator who
150
+ * made the URL wrote it.
151
+ *
152
+ * It has to travel, because the page has to name the account before anything
153
+ * is created and has no other way to learn it: a passkey manager keeps the
154
+ * name the ceremony was given and shows it wherever the key is listed, so a
155
+ * URL that carried none would put the user handle — sixteen random bytes — in
156
+ * front of the person at every sign-in.
157
+ *
158
+ * Apart from `issued_label`, which is a note about who the URL was handed to
159
+ * and stays on the credential as exactly that. One value answering both would
160
+ * be an administrator's private memo shown to the person as their own name,
161
+ * with no way to correct either without the other.
162
+ *
163
+ * A starting point and not the answer: the registration form shows it and the
164
+ * person may say otherwise, and `auth.register.display_name` is what they
165
+ * settled. For a URL adding a passkey to somebody who exists, it is the name
166
+ * they already read themselves by, so a second key joins the same account. */
167
+ display_name: Type.Optional(Type.String({ maxLength: 128 })),
168
+ /** Every instance this enrolment hands the person, written by whichever one
169
+ * the ceremony lands on once it succeeds.
170
+ *
171
+ * The set is decided at the terminal that made the URL, because that is where
172
+ * "the instances this one knows of" is a question anybody can see the answer
173
+ * to. It has to travel, for the same reason the rest of the claims do: behind
174
+ * a load balancer the ceremony lands wherever it lands, and an instance that
175
+ * wrote the grantings it happened to know of would answer a different
176
+ * question than the one that was asked.
177
+ *
178
+ * Written when the enrolment succeeds and not before. A granting for somebody
179
+ * who never registered would sit in the replicated set naming a person no
180
+ * user record answers for — inert, since nothing could authenticate as them,
181
+ * but there to be read and impossible to tell from one that means something.
182
+ *
183
+ * Absent is the issuer's own instance alone, which is what `instance` already
184
+ * says. Naming it here as well is how a URL hands over more than one. */
185
+ instances: Type.Optional(Type.Array(InstanceId)),
149
186
  } as const;
150
187
 
151
188
  /** What an enrolment URL carries, as the instance that issued it reads it back.
@@ -220,6 +257,19 @@ export const AuthRegisterArgs = Type.Object({
220
257
  /** What the person calls the device they are registering, for their own use
221
258
  * when they later read back a list of several. Nothing is decided by it. */
222
259
  device_label: Type.Optional(Type.String({ maxLength: 128 })),
260
+ /** What the person settled on being called, from the form the registration
261
+ * page showed them with the URL's `display_name` already in it.
262
+ *
263
+ * Here rather than left to the claims because the person is the one it is
264
+ * about: an administrator issuing the URL guesses at a name, and the person
265
+ * in front of the browser is who can say. It is what the authenticator was
266
+ * given as the account name, so what they see in their passkey manager and
267
+ * what they read themselves by here are the same string.
268
+ *
269
+ * Unstated leaves the claims' value standing. It authenticates nothing, like
270
+ * every other name here. Adding a passkey to somebody who already exists does
271
+ * not rename them — the account it joins is one they have already named. */
272
+ display_name: Type.Optional(Type.String({ maxLength: 128 })),
223
273
  /** The challenge this registration answers, with the instance that can spend
224
274
  * it — the same pairing an assertion carries, and for the same reason: the
225
275
  * value also sits inside `client_data_json`, but who may consume it does not,
@@ -212,6 +212,7 @@ export const AUTH_REGISTER_REQUEST: Static<typeof AuthRegisterRequest> = {
212
212
  token: ENROLL_TOKEN,
213
213
  code: ENROLL_CODE,
214
214
  device_label: "work laptop",
215
+ display_name: "kawaz",
215
216
  challenge: CHALLENGE,
216
217
  credential: {
217
218
  id: "Y3JlZC1pZA",
@@ -390,9 +391,10 @@ export const AUTH_RESOLVE_REQUEST = {
390
391
  } satisfies Static<typeof AuthResolveRequest>;
391
392
 
392
393
  /** What the issuer answers about a URL that makes a person. It names the user
393
- * the credential will be created against, and posts to the address in front of
394
- * the instances rather than to the issuer's own — whichever of them the answer
395
- * lands on completes it. */
394
+ * the credential will be created against, posts to the address in front of the
395
+ * instances rather than to the issuer's own — whichever of them the answer
396
+ * lands on completes it — and names both instances the person is to own, which
397
+ * the receiver writes the grantings for. */
396
398
  export const AUTH_RESOLVE_RESPONSE = {
397
399
  ok: true,
398
400
  request_id,
@@ -407,6 +409,8 @@ export const AUTH_RESOLVE_RESPONSE = {
407
409
  jti: "01J9Z3W2Q",
408
410
  user: USER,
409
411
  issued_label: "for kawaz",
412
+ display_name: "kawaz",
413
+ instances: [instance, other_instance],
410
414
  },
411
415
  } satisfies Static<typeof AuthResolveResponse>;
412
416
 
@@ -425,6 +429,8 @@ export const AUTH_RESOLVE_ADD_OWNER_RESPONSE = {
425
429
  expires_at: FIXTURE_NOW + 600_000,
426
430
  jti: "01J9Z3W2R",
427
431
  issued_label: "nuc, for kawaz",
432
+ display_name: "kawaz",
433
+ instances: [other_instance],
428
434
  },
429
435
  } satisfies Static<typeof AuthResolveResponse>;
430
436