@ccmsg/protocol 2.3.0 → 2.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/attributes.ts +53 -13
- package/src/common/auth.ts +502 -260
- package/src/errors.ts +10 -0
- package/src/fixtures/common.ts +166 -41
- package/src/fixtures/ids.ts +20 -8
- package/src/fixtures/index.ts +13 -1
- package/src/fixtures/topics.ts +114 -25
- package/src/identifiers.ts +33 -51
- package/src/schemas.ts +18 -3
package/package.json
CHANGED
package/src/attributes.ts
CHANGED
|
@@ -39,10 +39,12 @@ export interface OpAttributes {
|
|
|
39
39
|
* cannot, and they answer before any identity is settled. The route each is
|
|
40
40
|
* published at belongs to the instance, not here.
|
|
41
41
|
*
|
|
42
|
-
* Being reachable from a page is also what gives the
|
|
42
|
+
* Being reachable from a page is also what gives the four that settle an
|
|
43
43
|
* identity the only headers this contract reads over HTTP: the `Origin` a
|
|
44
|
-
* browser states, held to the origin
|
|
45
|
-
*
|
|
44
|
+
* browser states, held to the enrolment URL's origin on the two that spend one
|
|
45
|
+
* (`auth.register`, `auth.enroll`) and to the credential's or the family's on
|
|
46
|
+
* the two that do not (`auth.assert`, `auth.token.refresh`), and
|
|
47
|
+
* `Sec-Fetch-Site`, which has to be one of
|
|
46
48
|
* `same-origin`, `same-site` or `cross-site` — the three that say a page made
|
|
47
49
|
* the call. Anything else fails: `none`, which is a request with no initiator
|
|
48
50
|
* at all, a header that is absent, and a value this contract does not know.
|
|
@@ -70,7 +72,7 @@ const INSTANCE_ONLY = ["instance"] as const;
|
|
|
70
72
|
* facts live: authorization, capability gating and forwarding all read it
|
|
71
73
|
* rather than each carrying their own copy. */
|
|
72
74
|
export const OP_ATTRIBUTES = {
|
|
73
|
-
// --- common: connect, declare the end, and subscribe (
|
|
75
|
+
// --- common: connect, declare the end, and subscribe (18) ---
|
|
74
76
|
// A greeting settles what the connection is, and there is one per role: what
|
|
75
77
|
// each must carry is then the op's own schema rather than a rule read off a
|
|
76
78
|
// field, and a connection cannot be settled as something neither side meant.
|
|
@@ -138,7 +140,7 @@ export const OP_ATTRIBUTES = {
|
|
|
138
140
|
errors: ["topic_unknown"],
|
|
139
141
|
},
|
|
140
142
|
|
|
141
|
-
// The
|
|
143
|
+
// The five ops that authenticate a person are open to every role for the
|
|
142
144
|
// same reason the greetings are: they run before there is an identity to
|
|
143
145
|
// check, and what they answer is what settles one. They are `any_instance` because whichever
|
|
144
146
|
// instance is reached answers — behind a load balancer that is not a choice
|
|
@@ -168,6 +170,17 @@ export const OP_ATTRIBUTES = {
|
|
|
168
170
|
carrier: "http",
|
|
169
171
|
errors: ["auth_invalid", "auth_expired", "auth_unknown_issuer"],
|
|
170
172
|
},
|
|
173
|
+
// Adding an instance to a person who already exists: the same enrolment URL
|
|
174
|
+
// and the same six digits as a registration, answered by an assertion instead
|
|
175
|
+
// of a new credential. Its attributes are the registration's for that reason.
|
|
176
|
+
"auth.enroll": {
|
|
177
|
+
plane: "common",
|
|
178
|
+
roles: ALL_ROLES,
|
|
179
|
+
needs_hello: false,
|
|
180
|
+
locality: "any_instance",
|
|
181
|
+
carrier: "http",
|
|
182
|
+
errors: ["auth_invalid", "auth_expired", "auth_unknown_issuer"],
|
|
183
|
+
},
|
|
171
184
|
"auth.token.refresh": {
|
|
172
185
|
plane: "common",
|
|
173
186
|
roles: ALL_ROLES,
|
|
@@ -185,17 +198,44 @@ export const OP_ATTRIBUTES = {
|
|
|
185
198
|
locality: "any_instance",
|
|
186
199
|
errors: ["auth_invalid", "auth_expired"],
|
|
187
200
|
},
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
// `
|
|
191
|
-
|
|
201
|
+
// Reads back the caller's own account, which the connection already settled
|
|
202
|
+
// who is — so there is nothing further to check and nothing to name.
|
|
203
|
+
// `scope: "role"` because the role is what decides the reply's contents: it
|
|
204
|
+
// is the caller's own user, passkeys and instances and nobody else's.
|
|
205
|
+
"auth.account.read": {
|
|
192
206
|
plane: "common",
|
|
193
|
-
roles:
|
|
207
|
+
roles: USER_ONLY,
|
|
194
208
|
needs_hello: true,
|
|
195
|
-
locality: "
|
|
196
|
-
|
|
209
|
+
locality: "any_instance",
|
|
210
|
+
scope: "role",
|
|
211
|
+
errors: [],
|
|
212
|
+
},
|
|
213
|
+
// Letting go of one of the two things a person holds. Both act on the
|
|
214
|
+
// caller's own records and nobody else's, which is what `scope: "role"` says
|
|
215
|
+
// here as it does on the read; what is removed is named, and who it belongs
|
|
216
|
+
// to is the connection. `auth_in_use` is the refusal when the thing named is
|
|
217
|
+
// what the call is being made with.
|
|
218
|
+
"auth.ownership.remove": {
|
|
219
|
+
plane: "common",
|
|
220
|
+
roles: USER_ONLY,
|
|
221
|
+
needs_hello: true,
|
|
222
|
+
locality: "any_instance",
|
|
223
|
+
scope: "role",
|
|
224
|
+
errors: ["not_found", "auth_in_use"],
|
|
197
225
|
},
|
|
198
|
-
"auth.
|
|
226
|
+
"auth.credential.remove": {
|
|
227
|
+
plane: "common",
|
|
228
|
+
roles: USER_ONLY,
|
|
229
|
+
needs_hello: true,
|
|
230
|
+
locality: "any_instance",
|
|
231
|
+
scope: "role",
|
|
232
|
+
errors: ["not_found", "auth_in_use"],
|
|
233
|
+
},
|
|
234
|
+
// Between instances: what an issuer alone can answer — an enrolment URL's
|
|
235
|
+
// secret and a challenge that may be spent once. `owner_instance` because
|
|
236
|
+
// both live in one instance's memory, and it is reached by `to_instance`
|
|
237
|
+
// being that instance's id.
|
|
238
|
+
"auth.resolve": {
|
|
199
239
|
plane: "common",
|
|
200
240
|
roles: INSTANCE_ONLY,
|
|
201
241
|
needs_hello: true,
|