@kubun/service-credential-api 0.17.2 → 0.18.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/lib/adapter.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type Client } from '@enkaku/client';
2
- import type { AliasBindingCredentialProvider, CredentialProvider, ProviderAccount } from '@kubun/credential-types';
2
+ import type { AdoptGrantAPI, AliasBindingCredentialProvider, CredentialProvider, EntryPairedCredentialProvider, ProviderAccount, VersionedEntryCredentialProvider } from '@kubun/credential-types';
3
3
  import type { CredentialServiceProtocol } from './index.js';
4
4
  /**
5
5
  * What a storage-only account peek discloses: the provider account and the
@@ -22,4 +22,4 @@ export type CredentialProviderWithProvenance = CredentialProvider & {
22
22
  writerDID: string | null;
23
23
  } | null>;
24
24
  };
25
- export declare function toCredentialProvider(client: Client<CredentialServiceProtocol>): CredentialProviderWithProvenance & AliasBindingCredentialProvider & Required<Pick<CredentialProviderWithProvenance, 'peekAccount'>>;
25
+ export declare function toCredentialProvider(client: Client<CredentialServiceProtocol>): CredentialProviderWithProvenance & AliasBindingCredentialProvider & EntryPairedCredentialProvider & VersionedEntryCredentialProvider & AdoptGrantAPI & Required<Pick<CredentialProviderWithProvenance, 'peekAccount'>>;
package/lib/adapter.js CHANGED
@@ -6,7 +6,8 @@
6
6
  import { RequestError } from '@enkaku/client';
7
7
  import { ErrorCodes } from '@enkaku/protocol';
8
8
  import { deserializeCredential, serializeCredential } from '@kubun/credential';
9
- import { CredentialPeekUnsupportedError } from './errors.js';
9
+ import { CredentialUpdateUnavailableError } from '@kubun/credential-types';
10
+ import { CredentialPeekUnsupportedError, CredentialUnexpectedSupersededError } from './errors.js';
10
11
  export function toCredentialProvider(client) {
11
12
  return {
12
13
  async get (providerName, ownerDID) {
@@ -18,6 +19,80 @@ export function toCredentialProvider(client) {
18
19
  });
19
20
  return raw == null ? null : deserializeCredential(raw);
20
21
  },
22
+ async getCredentialRef (providerName, ownerDID) {
23
+ const ref = await client.request('credential/get-ref', {
24
+ param: {
25
+ providerName,
26
+ ownerDID
27
+ }
28
+ });
29
+ return ref == null ? null : {
30
+ credential: deserializeCredential(ref.credential),
31
+ entryID: ref.entryID
32
+ };
33
+ },
34
+ async getVersionedCredentialRef (providerName, ownerDID) {
35
+ const ref = await client.request('credential/get-ref', {
36
+ param: {
37
+ providerName,
38
+ ownerDID
39
+ }
40
+ });
41
+ return ref == null ? null : {
42
+ credential: deserializeCredential(ref.credential),
43
+ entryID: ref.entryID,
44
+ ...ref.version == null ? {} : {
45
+ version: ref.version
46
+ }
47
+ };
48
+ },
49
+ setCredentialForEntryIfUnchanged ({ providerName, ownerDID, entryID, credential, expectedVersion }) {
50
+ return client.request('credential/refresh-entry', {
51
+ param: {
52
+ providerName,
53
+ ownerDID,
54
+ entryID,
55
+ credential: serializeCredential(credential),
56
+ expectedVersion
57
+ }
58
+ });
59
+ },
60
+ async update (address, change) {
61
+ for(;;){
62
+ const ref = await client.request('credential/get-ref', {
63
+ param: {
64
+ providerName: address.providerName,
65
+ ownerDID: address.ownerDID,
66
+ deviceDID: address.deviceDID
67
+ }
68
+ });
69
+ if (ref == null || ref.version == null) throw new CredentialUpdateUnavailableError();
70
+ const result = await client.request('credential/refresh-entry', {
71
+ param: {
72
+ providerName: address.providerName,
73
+ ownerDID: address.ownerDID,
74
+ deviceDID: address.deviceDID,
75
+ entryID: ref.entryID,
76
+ credential: serializeCredential(change(deserializeCredential(ref.credential))),
77
+ expectedVersion: ref.version
78
+ }
79
+ });
80
+ if (result === 'updated') return;
81
+ if (result === 'absent') throw new CredentialUpdateUnavailableError();
82
+ }
83
+ },
84
+ async setCredentialForEntry ({ providerName, ownerDID, entryID, credential }) {
85
+ const result = await client.request('credential/refresh-entry', {
86
+ param: {
87
+ providerName,
88
+ ownerDID,
89
+ entryID,
90
+ credential: serializeCredential(credential)
91
+ }
92
+ });
93
+ if (result === 'superseded') throw new CredentialUnexpectedSupersededError();
94
+ return result;
95
+ },
21
96
  async peekAccount (providerName, ownerDID) {
22
97
  let peeked;
23
98
  try {
@@ -97,6 +172,11 @@ export function toCredentialProvider(client) {
97
172
  entryID
98
173
  }
99
174
  });
175
+ },
176
+ async adoptCredentialGrant (params) {
177
+ await client.request('credential/adopt-grant', {
178
+ param: params
179
+ });
100
180
  }
101
181
  };
102
182
  }
package/lib/errors.d.ts CHANGED
@@ -16,3 +16,10 @@ export declare class CredentialPeekUnsupportedError extends Error {
16
16
  constructor(options?: ErrorOptions);
17
17
  get code(): typeof CREDENTIAL_PEEK_UNSUPPORTED;
18
18
  }
19
+ /** An unconditional entry write received a result reserved for versioned writes. */
20
+ export declare const CREDENTIAL_UNEXPECTED_SUPERSEDED = "CREDENTIAL_UNEXPECTED_SUPERSEDED";
21
+ export declare class CredentialUnexpectedSupersededError extends Error {
22
+ #private;
23
+ constructor();
24
+ get code(): typeof CREDENTIAL_UNEXPECTED_SUPERSEDED;
25
+ }
package/lib/errors.js CHANGED
@@ -20,3 +20,15 @@
20
20
  return this.#code;
21
21
  }
22
22
  }
23
+ /** An unconditional entry write received a result reserved for versioned writes. */ export const CREDENTIAL_UNEXPECTED_SUPERSEDED = 'CREDENTIAL_UNEXPECTED_SUPERSEDED';
24
+ export class CredentialUnexpectedSupersededError extends Error {
25
+ #code;
26
+ constructor(){
27
+ super('The credential service superseded an unconditional entry write');
28
+ this.name = 'CredentialUnexpectedSupersededError';
29
+ this.#code = CREDENTIAL_UNEXPECTED_SUPERSEDED;
30
+ }
31
+ get code() {
32
+ return this.#code;
33
+ }
34
+ }
package/lib/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { type CredentialAccountPeek, type CredentialProviderWithProvenance, toCredentialProvider, } from './adapter.js';
2
- export { CREDENTIAL_PEEK_UNSUPPORTED, CredentialPeekUnsupportedError } from './errors.js';
2
+ export { CREDENTIAL_PEEK_UNSUPPORTED, CREDENTIAL_UNEXPECTED_SUPERSEDED, CredentialPeekUnsupportedError, CredentialUnexpectedSupersededError, } from './errors.js';
3
3
  export declare const credentialServiceProtocol: {
4
4
  readonly 'credential/get': {
5
5
  readonly type: "request";
@@ -24,6 +24,45 @@ export declare const credentialServiceProtocol: {
24
24
  }];
25
25
  };
26
26
  };
27
+ readonly 'credential/get-ref': {
28
+ readonly type: "request";
29
+ readonly param: {
30
+ readonly type: "object";
31
+ readonly properties: {
32
+ readonly providerName: {
33
+ readonly type: "string";
34
+ };
35
+ readonly ownerDID: {
36
+ readonly type: "string";
37
+ };
38
+ readonly deviceDID: {
39
+ readonly type: "string";
40
+ };
41
+ };
42
+ readonly required: readonly ["providerName", "ownerDID"];
43
+ readonly additionalProperties: false;
44
+ };
45
+ readonly result: {
46
+ readonly anyOf: [{
47
+ readonly type: "object";
48
+ readonly properties: {
49
+ readonly credential: {
50
+ readonly type: "string";
51
+ };
52
+ readonly entryID: {
53
+ readonly type: "string";
54
+ };
55
+ readonly version: {
56
+ readonly type: "string";
57
+ };
58
+ };
59
+ readonly required: readonly ["credential", "entryID"];
60
+ readonly additionalProperties: false;
61
+ }, {
62
+ readonly type: "null";
63
+ }];
64
+ };
65
+ };
27
66
  readonly 'credential/peek': {
28
67
  readonly type: "request";
29
68
  readonly param: {
@@ -165,6 +204,73 @@ export declare const credentialServiceProtocol: {
165
204
  readonly type: "null";
166
205
  };
167
206
  };
207
+ readonly 'credential/refresh-entry': {
208
+ readonly type: "request";
209
+ readonly param: {
210
+ readonly type: "object";
211
+ readonly properties: {
212
+ readonly providerName: {
213
+ readonly type: "string";
214
+ };
215
+ readonly ownerDID: {
216
+ readonly type: "string";
217
+ };
218
+ readonly deviceDID: {
219
+ readonly type: "string";
220
+ };
221
+ readonly entryID: {
222
+ readonly type: "string";
223
+ };
224
+ readonly credential: {
225
+ readonly type: "string";
226
+ };
227
+ readonly expectedVersion: {
228
+ readonly type: "string";
229
+ };
230
+ };
231
+ readonly required: readonly ["providerName", "ownerDID", "entryID", "credential"];
232
+ readonly additionalProperties: false;
233
+ };
234
+ readonly result: {
235
+ readonly type: "string";
236
+ readonly enum: readonly ["updated", "absent", "superseded"];
237
+ };
238
+ };
239
+ readonly 'credential/adopt-grant': {
240
+ readonly type: "request";
241
+ readonly param: {
242
+ readonly type: "object";
243
+ readonly properties: {
244
+ readonly providerName: {
245
+ readonly type: "string";
246
+ };
247
+ readonly ownerDID: {
248
+ readonly type: "string";
249
+ };
250
+ readonly fromDeviceDID: {
251
+ readonly type: "string";
252
+ };
253
+ readonly toDeviceDID: {
254
+ readonly type: "string";
255
+ };
256
+ readonly sourceKeyOwnerDID: {
257
+ readonly type: "string";
258
+ };
259
+ };
260
+ readonly required: readonly ["providerName", "ownerDID", "fromDeviceDID", "toDeviceDID"];
261
+ readonly additionalProperties: false;
262
+ };
263
+ readonly result: {
264
+ readonly type: "object";
265
+ readonly properties: {
266
+ readonly adopted: {
267
+ readonly type: "boolean";
268
+ };
269
+ };
270
+ readonly required: readonly ["adopted"];
271
+ readonly additionalProperties: false;
272
+ };
273
+ };
168
274
  readonly 'credential/delete': {
169
275
  readonly type: "request";
170
276
  readonly param: {
package/lib/index.js CHANGED
@@ -5,14 +5,15 @@
5
5
  // authenticated client into the `credential-provider` the connector registers, so
6
6
  // this package is no longer type-only.
7
7
  export { toCredentialProvider } from './adapter.js';
8
- export { CREDENTIAL_PEEK_UNSUPPORTED, CredentialPeekUnsupportedError } from './errors.js';
8
+ export { CREDENTIAL_PEEK_UNSUPPORTED, CREDENTIAL_UNEXPECTED_SUPERSEDED, CredentialPeekUnsupportedError, CredentialUnexpectedSupersededError } from './errors.js';
9
9
  // Param↔seam mapping. `providerName`/`ownerDID` are the alias key the seam
10
10
  // resolves. `ownerDID` is ALSO the authorization subject: each handler passes it
11
11
  // to `context.authorize(ownerDID)` (the per-serve `callerMayActFor`), NOT the
12
12
  // Enkaku message `sub` — the host rejects a `sub` that differs from `iss`, so a
13
13
  // caller acting for another owner carries that owner in the param, not the
14
14
  // envelope. Serialized credential bytes cross as a UTF string; the vault stores
15
- // the ciphertext, never the plaintext. Writes ack with `null`.
15
+ // the ciphertext, never the plaintext. Entry-scoped writes report their outcome;
16
+ // the other writes ack with `null`.
16
17
  export const credentialServiceProtocol = {
17
18
  'credential/get': {
18
19
  type: 'request',
@@ -43,6 +44,54 @@ export const credentialServiceProtocol = {
43
44
  ]
44
45
  }
45
46
  },
47
+ 'credential/get-ref': {
48
+ type: 'request',
49
+ param: {
50
+ type: 'object',
51
+ properties: {
52
+ providerName: {
53
+ type: 'string'
54
+ },
55
+ ownerDID: {
56
+ type: 'string'
57
+ },
58
+ deviceDID: {
59
+ type: 'string'
60
+ }
61
+ },
62
+ required: [
63
+ 'providerName',
64
+ 'ownerDID'
65
+ ],
66
+ additionalProperties: false
67
+ },
68
+ result: {
69
+ anyOf: [
70
+ {
71
+ type: 'object',
72
+ properties: {
73
+ credential: {
74
+ type: 'string'
75
+ },
76
+ entryID: {
77
+ type: 'string'
78
+ },
79
+ version: {
80
+ type: 'string'
81
+ }
82
+ },
83
+ required: [
84
+ 'credential',
85
+ 'entryID'
86
+ ],
87
+ additionalProperties: false
88
+ },
89
+ {
90
+ type: 'null'
91
+ }
92
+ ]
93
+ }
94
+ },
46
95
  // Storage-only, non-refreshing read of the stored credential's provider
47
96
  // account and the scopes it was granted — and nothing else: no token
48
97
  // material crosses the wire. The server
@@ -234,6 +283,89 @@ export const credentialServiceProtocol = {
234
283
  type: 'null'
235
284
  }
236
285
  },
286
+ 'credential/refresh-entry': {
287
+ type: 'request',
288
+ param: {
289
+ type: 'object',
290
+ properties: {
291
+ providerName: {
292
+ type: 'string'
293
+ },
294
+ ownerDID: {
295
+ type: 'string'
296
+ },
297
+ deviceDID: {
298
+ type: 'string'
299
+ },
300
+ entryID: {
301
+ type: 'string'
302
+ },
303
+ credential: {
304
+ type: 'string'
305
+ },
306
+ expectedVersion: {
307
+ type: 'string'
308
+ }
309
+ },
310
+ required: [
311
+ 'providerName',
312
+ 'ownerDID',
313
+ 'entryID',
314
+ 'credential'
315
+ ],
316
+ additionalProperties: false
317
+ },
318
+ result: {
319
+ type: 'string',
320
+ enum: [
321
+ 'updated',
322
+ 'absent',
323
+ 'superseded'
324
+ ]
325
+ }
326
+ },
327
+ 'credential/adopt-grant': {
328
+ type: 'request',
329
+ param: {
330
+ type: 'object',
331
+ properties: {
332
+ providerName: {
333
+ type: 'string'
334
+ },
335
+ ownerDID: {
336
+ type: 'string'
337
+ },
338
+ fromDeviceDID: {
339
+ type: 'string'
340
+ },
341
+ toDeviceDID: {
342
+ type: 'string'
343
+ },
344
+ sourceKeyOwnerDID: {
345
+ type: 'string'
346
+ }
347
+ },
348
+ required: [
349
+ 'providerName',
350
+ 'ownerDID',
351
+ 'fromDeviceDID',
352
+ 'toDeviceDID'
353
+ ],
354
+ additionalProperties: false
355
+ },
356
+ result: {
357
+ type: 'object',
358
+ properties: {
359
+ adopted: {
360
+ type: 'boolean'
361
+ }
362
+ },
363
+ required: [
364
+ 'adopted'
365
+ ],
366
+ additionalProperties: false
367
+ }
368
+ },
237
369
  'credential/delete': {
238
370
  type: 'request',
239
371
  param: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/service-credential-api",
3
- "version": "0.17.2",
3
+ "version": "0.18.0",
4
4
  "keywords": [],
5
5
  "license": "see LICENSE.md",
6
6
  "sideEffects": false,
@@ -15,10 +15,10 @@
15
15
  "LICENSE.md"
16
16
  ],
17
17
  "dependencies": {
18
- "@enkaku/client": "^0.21.2",
19
- "@enkaku/protocol": "^0.21.1",
20
- "@kubun/credential": "^0.17.3",
21
- "@kubun/credential-types": "^0.17.2"
18
+ "@enkaku/client": "^0.21.5",
19
+ "@enkaku/protocol": "^0.21.5",
20
+ "@kubun/credential": "^0.18.0",
21
+ "@kubun/credential-types": "^0.18.0"
22
22
  },
23
23
  "publishConfig": {
24
24
  "access": "public"