@ixo/ucan 1.0.0 → 1.1.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/.turbo/turbo-build.log +1 -1
- package/README.md +215 -117
- package/dist/capabilities/capability.d.ts +2 -2
- package/dist/capabilities/capability.d.ts.map +1 -1
- package/dist/capabilities/capability.js.map +1 -1
- package/dist/client/create-client.d.ts +1 -0
- package/dist/client/create-client.d.ts.map +1 -1
- package/dist/client/create-client.js +6 -3
- package/dist/client/create-client.js.map +1 -1
- package/dist/did/ixo-resolver.d.ts.map +1 -1
- package/dist/did/ixo-resolver.js.map +1 -1
- package/dist/store/memory.d.ts.map +1 -1
- package/dist/store/memory.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/validator/validator.d.ts +3 -1
- package/dist/validator/validator.d.ts.map +1 -1
- package/dist/validator/validator.js +25 -0
- package/dist/validator/validator.js.map +1 -1
- package/docs/FLOW.md +287 -0
- package/docs/examples/CLIENT.md +418 -0
- package/docs/examples/SERVER.md +419 -0
- package/package.json +6 -8
- package/scripts/test-ucan.ts +31 -19
- package/src/capabilities/capability.ts +8 -7
- package/src/client/create-client.ts +29 -11
- package/src/did/ixo-resolver.ts +4 -6
- package/src/did/utils.ts +0 -1
- package/src/store/memory.ts +4 -2
- package/src/validator/validator.test.ts +611 -0
- package/src/validator/validator.ts +67 -7
- package/tsconfig.json +1 -1
- package/vitest.config.ts +2 -0
- package/.eslintrc.js +0 -9
- package/.prettierignore +0 -3
- package/.prettierrc.js +0 -4
- package/CHANGELOG.md +0 -0
- package/jest.config.js +0 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
1
2
|
/**
|
|
2
3
|
* @fileoverview Framework-agnostic UCAN validator
|
|
3
4
|
*
|
|
@@ -14,13 +15,13 @@
|
|
|
14
15
|
import { ed25519 } from '@ucanto/principal';
|
|
15
16
|
import { Delegation } from '@ucanto/core';
|
|
16
17
|
import { claim } from '@ucanto/validator';
|
|
17
|
-
import { capability } from '@ucanto/validator';
|
|
18
|
+
import { type capability } from '@ucanto/validator';
|
|
18
19
|
import type { DIDKeyResolver, InvocationStore } from '../types.js';
|
|
19
20
|
import { InMemoryInvocationStore } from '../store/memory.js';
|
|
20
21
|
|
|
21
22
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
23
|
type CapabilityParser = ReturnType<typeof capability<any, any, any>>;
|
|
23
|
-
|
|
24
|
+
|
|
24
25
|
type Verifier = ReturnType<typeof ed25519.Verifier.parse>;
|
|
25
26
|
|
|
26
27
|
/**
|
|
@@ -76,6 +77,21 @@ export interface ValidateResult {
|
|
|
76
77
|
nb?: Record<string, unknown>;
|
|
77
78
|
};
|
|
78
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Effective expiration as Unix timestamp (seconds).
|
|
82
|
+
* This is the earliest expiration across the entire delegation chain,
|
|
83
|
+
* i.e. when the authorization effectively expires.
|
|
84
|
+
* Undefined if no expiration is set (never expires).
|
|
85
|
+
*/
|
|
86
|
+
expiration?: number;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The delegation chain from root issuer to invoker.
|
|
90
|
+
* e.g. ["did:key:root", "did:key:alice", "did:key:bob"]
|
|
91
|
+
* For a direct root invocation (no delegation), this is just ["did:key:root"].
|
|
92
|
+
*/
|
|
93
|
+
proofChain?: string[];
|
|
94
|
+
|
|
79
95
|
/** Error details (if invalid) */
|
|
80
96
|
error?: {
|
|
81
97
|
code:
|
|
@@ -227,9 +243,7 @@ export async function createUCANValidator(
|
|
|
227
243
|
|
|
228
244
|
// Try custom resolver for other DID methods (e.g., did:ixo)
|
|
229
245
|
if (options.didResolver) {
|
|
230
|
-
const result = await options.didResolver(
|
|
231
|
-
did as `did:${string}:${string}`,
|
|
232
|
-
);
|
|
246
|
+
const result = await options.didResolver(did);
|
|
233
247
|
if ('ok' in result && result.ok.length > 0) {
|
|
234
248
|
// Return the array of did:key strings (ucanto will parse them)
|
|
235
249
|
return { ok: result.ok };
|
|
@@ -254,6 +268,43 @@ export async function createUCANValidator(
|
|
|
254
268
|
};
|
|
255
269
|
};
|
|
256
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Build the delegation chain as an array of DIDs from root to invoker.
|
|
273
|
+
* Recursively traverses the first proof of each delegation.
|
|
274
|
+
*/
|
|
275
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- delegation type from Delegation.extract() is complex
|
|
276
|
+
function buildProofChain(delegation: any): string[] {
|
|
277
|
+
if (!delegation?.proofs || delegation.proofs.length === 0) {
|
|
278
|
+
return [delegation.issuer.did()];
|
|
279
|
+
}
|
|
280
|
+
const parentChain = buildProofChain(delegation.proofs[0]);
|
|
281
|
+
return [...parentChain, delegation.issuer.did()];
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Compute the effective (earliest) expiration across the entire delegation chain.
|
|
286
|
+
* Returns undefined if no expiration is set anywhere in the chain.
|
|
287
|
+
*/
|
|
288
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
289
|
+
function computeEffectiveExpiration(delegation: any): number | undefined {
|
|
290
|
+
const exp =
|
|
291
|
+
typeof delegation?.expiration === 'number' &&
|
|
292
|
+
isFinite(delegation.expiration)
|
|
293
|
+
? delegation.expiration
|
|
294
|
+
: undefined;
|
|
295
|
+
|
|
296
|
+
if (!delegation?.proofs || delegation.proofs.length === 0) {
|
|
297
|
+
return exp;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const parentExp = computeEffectiveExpiration(delegation.proofs[0]);
|
|
301
|
+
|
|
302
|
+
if (exp !== undefined && parentExp !== undefined) {
|
|
303
|
+
return Math.min(exp, parentExp);
|
|
304
|
+
}
|
|
305
|
+
return exp ?? parentExp;
|
|
306
|
+
}
|
|
307
|
+
|
|
257
308
|
return {
|
|
258
309
|
serverDid: options.serverDid,
|
|
259
310
|
|
|
@@ -264,7 +315,9 @@ export async function createUCANValidator(
|
|
|
264
315
|
): Promise<ValidateResult> {
|
|
265
316
|
try {
|
|
266
317
|
// 1. Decode the invocation from base64 CAR
|
|
267
|
-
const carBytes = new Uint8Array(
|
|
318
|
+
const carBytes = new Uint8Array(
|
|
319
|
+
Buffer.from(invocationBase64, 'base64'),
|
|
320
|
+
);
|
|
268
321
|
|
|
269
322
|
// 2. Extract the invocation from CAR
|
|
270
323
|
const extracted = await Delegation.extract(carBytes);
|
|
@@ -320,8 +373,9 @@ export async function createUCANValidator(
|
|
|
320
373
|
const claimResult = claim(capabilityDef, [invocation], {
|
|
321
374
|
authority: serverVerifier,
|
|
322
375
|
principal: ed25519.Verifier,
|
|
376
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ucanto claim() expects a specific DID resolver signature incompatible with our async resolver
|
|
323
377
|
resolveDIDKey: resolveDIDKey as any,
|
|
324
|
-
canIssue: (cap:
|
|
378
|
+
canIssue: (cap: { with: string }, issuer: string) => {
|
|
325
379
|
// Root issuers can issue any capability
|
|
326
380
|
if (options.rootIssuers.includes(issuer)) return true;
|
|
327
381
|
// Allow self-issued capabilities where resource contains issuer DID
|
|
@@ -379,6 +433,10 @@ export async function createUCANValidator(
|
|
|
379
433
|
await invocationStore.add(invocationCid);
|
|
380
434
|
}
|
|
381
435
|
|
|
436
|
+
// 9. Build proof chain and compute effective expiration
|
|
437
|
+
const proofChain = buildProofChain(invocation);
|
|
438
|
+
const expiration = computeEffectiveExpiration(invocation);
|
|
439
|
+
|
|
382
440
|
return {
|
|
383
441
|
ok: true,
|
|
384
442
|
invoker: invocation.issuer.did(),
|
|
@@ -389,6 +447,8 @@ export async function createUCANValidator(
|
|
|
389
447
|
nb: validatedCap.nb as Record<string, unknown> | undefined,
|
|
390
448
|
}
|
|
391
449
|
: undefined,
|
|
450
|
+
expiration,
|
|
451
|
+
proofChain,
|
|
392
452
|
};
|
|
393
453
|
} catch (err) {
|
|
394
454
|
const message = err instanceof Error ? err.message : 'Unknown error';
|
package/tsconfig.json
CHANGED
package/vitest.config.ts
ADDED
package/.eslintrc.js
DELETED
package/.prettierignore
DELETED
package/.prettierrc.js
DELETED
package/CHANGELOG.md
DELETED
|
File without changes
|
package/jest.config.js
DELETED