iso-ucan 0.0.1
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/dist/src/capability.d.ts +49 -0
- package/dist/src/capability.d.ts.map +1 -0
- package/dist/src/client.d.ts +47 -0
- package/dist/src/client.d.ts.map +1 -0
- package/dist/src/delegation.d.ts +61 -0
- package/dist/src/delegation.d.ts.map +1 -0
- package/dist/src/envelope.d.ts +39 -0
- package/dist/src/envelope.d.ts.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/invocation.d.ts +46 -0
- package/dist/src/invocation.d.ts.map +1 -0
- package/dist/src/policy.d.ts +10 -0
- package/dist/src/policy.d.ts.map +1 -0
- package/dist/src/server.d.ts +5 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/store.d.ts +49 -0
- package/dist/src/store.d.ts.map +1 -0
- package/dist/src/types.d.ts +187 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/utils.d.ts +136 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/varsig.d.ts +24 -0
- package/dist/src/varsig.d.ts.map +1 -0
- package/package.json +140 -0
- package/readme.md +81 -0
- package/src/capability.js +77 -0
- package/src/client.js +52 -0
- package/src/delegation.js +183 -0
- package/src/envelope.js +88 -0
- package/src/index.js +1 -0
- package/src/invocation.js +227 -0
- package/src/policy.js +272 -0
- package/src/server.ts +24 -0
- package/src/store.js +187 -0
- package/src/types/envelope.ts +131 -0
- package/src/types/policy.ts +100 -0
- package/src/types/varsig.ts +22 -0
- package/src/types.ts +243 -0
- package/src/utils.js +375 -0
- package/src/varsig.js +157 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import {PayloadSpec} from './types.js'
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Create the signature payload for a given envelope
|
|
6
|
+
*
|
|
7
|
+
* @param {import('./types.js').DecodedEnvelope<PayloadSpec>} envelope
|
|
8
|
+
* @returns
|
|
9
|
+
*/
|
|
10
|
+
export function signaturePayload(envelope: import("./types.js").DecodedEnvelope<PayloadSpec>): {
|
|
11
|
+
[x: string]: Uint8Array<ArrayBuffer> | import("./types.js").DelegationPayload<unknown> | import("./types.js").InvocationPayload;
|
|
12
|
+
h: Uint8Array<ArrayBuffer>;
|
|
13
|
+
};
|
|
14
|
+
export function nowInSeconds(): number;
|
|
15
|
+
/**
|
|
16
|
+
*
|
|
17
|
+
* @param {import('./types.js').DecodedEnvelope<PayloadSpec>} envelope
|
|
18
|
+
*/
|
|
19
|
+
export function cid(envelope: import("./types.js").DecodedEnvelope<PayloadSpec>): Promise<CID<any, 113, 18, 1>>;
|
|
20
|
+
/**
|
|
21
|
+
* Check if a DID and signature type are compatible
|
|
22
|
+
*
|
|
23
|
+
* @param {import('iso-did/types').VerifiableDID} did
|
|
24
|
+
* @param {import('iso-signatures/types').SignatureType} sigType
|
|
25
|
+
*/
|
|
26
|
+
export function isSigAndDidCompatible(did: import("iso-did/types").VerifiableDID, sigType: import("iso-signatures/types").SignatureType): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Validate the expiration of a UCAN
|
|
29
|
+
*
|
|
30
|
+
* @param {number | null} exp
|
|
31
|
+
*/
|
|
32
|
+
export function assertExpiration(exp: number | null): void;
|
|
33
|
+
/**
|
|
34
|
+
* Validate the not before time of a UCAN
|
|
35
|
+
*
|
|
36
|
+
* @param {number} [nbf]
|
|
37
|
+
*/
|
|
38
|
+
export function assertNotBefore(nbf?: number): void;
|
|
39
|
+
/**
|
|
40
|
+
* Validate the issuer and signature of a UCAN
|
|
41
|
+
*
|
|
42
|
+
* @param {import('./types.js').DecodedEnvelope<PayloadSpec>} envelope
|
|
43
|
+
* @param {import('iso-did').Resolver} [didResolver]
|
|
44
|
+
*/
|
|
45
|
+
export function validateIssuerAndSignature(envelope: import("./types.js").DecodedEnvelope<PayloadSpec>, didResolver?: import("iso-did").Resolver): Promise<DID>;
|
|
46
|
+
/**
|
|
47
|
+
* Verify the signature of a UCAN and that issuer is compatible with the signature
|
|
48
|
+
*
|
|
49
|
+
* @param {import('./types.js').DecodedEnvelope<PayloadSpec>} envelope
|
|
50
|
+
* @param {import('iso-signatures/verifiers/resolver.js').Resolver} signatureVerifierResolver
|
|
51
|
+
* @param {import('iso-did').Resolver} [didResolver]
|
|
52
|
+
*/
|
|
53
|
+
export function verifySignature(envelope: import("./types.js").DecodedEnvelope<PayloadSpec>, signatureVerifierResolver: import("iso-signatures/verifiers/resolver.js").Resolver, didResolver?: import("iso-did").Resolver): Promise<boolean>;
|
|
54
|
+
/**
|
|
55
|
+
* Asserts that a UCAN command string is syntactically valid.
|
|
56
|
+
* If the command is invalid, it throws a descriptive error.
|
|
57
|
+
*
|
|
58
|
+
* Rules:
|
|
59
|
+
* - Commands MUST begin with a slash (/).
|
|
60
|
+
* - Commands MUST be lowercase.
|
|
61
|
+
* - A trailing slash MUST NOT be present, unless the command is exactly "/".
|
|
62
|
+
* - Segments MUST be separated by a slash (e.g., no empty segments like "//").
|
|
63
|
+
*
|
|
64
|
+
* @param {string} command The command string to validate.
|
|
65
|
+
* @throws {Error} If the command is syntactically invalid.
|
|
66
|
+
* @returns {void} Does not return a value on success.
|
|
67
|
+
*/
|
|
68
|
+
export function assertIsValidCommand(command: string): void;
|
|
69
|
+
/**
|
|
70
|
+
* Assert that the input is a Uint8Array.
|
|
71
|
+
*
|
|
72
|
+
* @param {unknown} nonce The value to check.
|
|
73
|
+
* @throws {Error} If nonce is not a Uint8Array.
|
|
74
|
+
* @returns {void}
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts twoslash
|
|
78
|
+
* import { assertNonce } from 'iso-ucan/utils'
|
|
79
|
+
* assertNonce(new Uint8Array([1,2,3])) // ok
|
|
80
|
+
* assertNonce('foo') // throws
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export function assertNonce(nonce: unknown): void;
|
|
84
|
+
/**
|
|
85
|
+
* Assert that args is a JSON serializable object.
|
|
86
|
+
*
|
|
87
|
+
* @param {unknown} args
|
|
88
|
+
*/
|
|
89
|
+
export function assertArgs(args: unknown): void;
|
|
90
|
+
/**
|
|
91
|
+
* Assert that the input is a Record<PropertyKey, unknown>.
|
|
92
|
+
*
|
|
93
|
+
* @param {unknown} meta - The value to check.
|
|
94
|
+
* @throws {Error} If meta is not a Record<PropertyKey, unknown>.
|
|
95
|
+
* @returns {void}
|
|
96
|
+
*/
|
|
97
|
+
export function assertMeta(meta: unknown): void;
|
|
98
|
+
/**
|
|
99
|
+
* Assert that the input is not revoked.
|
|
100
|
+
*
|
|
101
|
+
* @param {import('multiformats/cid').CID} cid
|
|
102
|
+
* @param {(cid: CID) => Promise<boolean>} [isRevokedFn]
|
|
103
|
+
*/
|
|
104
|
+
export function assertNotRevoked(cid: import("multiformats/cid").CID, isRevokedFn?: (cid: CID) => Promise<boolean>): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* Expiration or TTL (default 300 seconds)
|
|
107
|
+
*
|
|
108
|
+
* @param {object} options
|
|
109
|
+
* @param {number | null} [options.exp]
|
|
110
|
+
* @param {number} [options.ttl]
|
|
111
|
+
*/
|
|
112
|
+
export function expOrTtl({ exp, ttl }: {
|
|
113
|
+
exp?: number | null | undefined;
|
|
114
|
+
ttl?: number | undefined;
|
|
115
|
+
}): number | null;
|
|
116
|
+
/**
|
|
117
|
+
* @param {unknown} policy
|
|
118
|
+
*/
|
|
119
|
+
export function assertPolicy(policy: unknown): void;
|
|
120
|
+
export const cborValue: z.ZodLazy<z.ZodType<import("./types.js").CborValue, any, z.core.$ZodTypeInternals<import("./types.js").CborValue, any>>>;
|
|
121
|
+
export const cborObject: z.ZodRecord<z.ZodString, z.ZodLazy<z.ZodType<import("./types.js").CborValue, any, z.core.$ZodTypeInternals<import("./types.js").CborValue, any>>>>;
|
|
122
|
+
export const selector: z.ZodUnion<readonly [z.ZodLiteral<".">, z.ZodTemplateLiteral<`.${string}`>]>;
|
|
123
|
+
/**
|
|
124
|
+
* @typedef {z.infer<typeof selector>} Selector
|
|
125
|
+
*/
|
|
126
|
+
/**
|
|
127
|
+
* Statement
|
|
128
|
+
*/
|
|
129
|
+
export const statement: z.ZodLazy<z.ZodType<import("./types.js").Statement<unknown>, any, z.core.$ZodTypeInternals<import("./types.js").Statement<unknown>, any>>>;
|
|
130
|
+
export const policySchema: z.ZodArray<z.ZodLazy<z.ZodType<import("./types.js").Statement<unknown>, any, z.core.$ZodTypeInternals<import("./types.js").Statement<unknown>, any>>>>;
|
|
131
|
+
export type Selector = z.infer<typeof selector>;
|
|
132
|
+
import type { PayloadSpec } from './types.js';
|
|
133
|
+
import { CID } from 'multiformats/cid';
|
|
134
|
+
import { DID } from 'iso-did';
|
|
135
|
+
import { z } from 'zod';
|
|
136
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.js"],"names":[],"mappings":"AASA;;GAEG;AAEH;;;;;GAKG;AACH,2CAHW,OAAO,YAAY,EAAE,eAAe,CAAC,WAAW,CAAC;;;EAe3D;AAED,uCAEC;AAED;;;GAGG;AACH,8BAFW,OAAO,YAAY,EAAE,eAAe,CAAC,WAAW,CAAC,iCAW3D;AAED;;;;;GAKG;AACH,2CAHW,OAAO,eAAe,EAAE,aAAa,WACrC,OAAO,sBAAsB,EAAE,aAAa,WA+BtD;AAED;;;;GAIG;AACH,sCAFW,MAAM,GAAG,IAAI,QAgBvB;AAED;;;;GAIG;AACH,sCAFW,MAAM,QAMhB;AAED;;;;;GAKG;AACH,qDAHW,OAAO,YAAY,EAAE,eAAe,CAAC,WAAW,CAAC,gBACjD,OAAO,SAAS,EAAE,QAAQ,gBAWpC;AAED;;;;;;GAMG;AACH,0CAJW,OAAO,YAAY,EAAE,eAAe,CAAC,WAAW,CAAC,6BACjD,OAAO,sCAAsC,EAAE,QAAQ,gBACvD,OAAO,SAAS,EAAE,QAAQ,oBAoBpC;AAED;;;;;;;;;;;;;GAaG;AACH,8CAJW,MAAM,GAEJ,IAAI,CAuChB;AAED;;;;;;;;;;;;;GAaG;AACH,mCAXW,OAAO,GAEL,IAAI,CAehB;AACD;;;;GAIG;AACH,iCAFW,OAAO,QASjB;AAED;;;;;;GAMG;AACH,iCAJW,OAAO,GAEL,IAAI,CAWhB;AAED;;;;;GAKG;AACH,sCAHW,OAAO,kBAAkB,EAAE,GAAG,gBAC9B,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,CAAC,iBAQxC;AAED;;;;;;GAMG;AACH,uCAHG;IAAgC,GAAG;IACV,GAAG;CAAC,iBAU/B;AAgED;;GAEG;AACH,qCAFW,OAAO,QASjB;AAxED,iJAeI;AACJ,4KAAyD;AAEzD,oGAGE;AAEF;;GAEG;AAEH;;GAEG;AACH,mKA4BI;AAEJ,kLAA8C;uBApCjC,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC;iCA5TP,YAAY;oBARtB,kBAAkB;oBADlB,SAAS;kBAGX,KAAK"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {import('./types.js').VarsigOptions} options
|
|
3
|
+
*/
|
|
4
|
+
export function encode(options: import("./types.js").VarsigOptions): Uint8Array<ArrayBuffer>;
|
|
5
|
+
/**
|
|
6
|
+
/**
|
|
7
|
+
* Decode varsig header
|
|
8
|
+
*
|
|
9
|
+
* @param {Uint8Array} buf
|
|
10
|
+
* @returns {import('./types.js').DecodeOutput}
|
|
11
|
+
*/
|
|
12
|
+
export function decode(buf: Uint8Array): import("./types.js").DecodeOutput;
|
|
13
|
+
export const VARSIG: 52;
|
|
14
|
+
export const VERSION: 1;
|
|
15
|
+
export const EIP191_ENCODING: 57745;
|
|
16
|
+
/**
|
|
17
|
+
* @type {Record<number, import('./types.js').VarsigEncoding>}
|
|
18
|
+
*/
|
|
19
|
+
export const CODE_ENCODING: Record<number, import("./types.js").VarsigEncoding>;
|
|
20
|
+
/**
|
|
21
|
+
* @type {Record<number, string>}
|
|
22
|
+
*/
|
|
23
|
+
export const SIG_PREFIX: Record<number, string>;
|
|
24
|
+
//# sourceMappingURL=varsig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"varsig.d.ts","sourceRoot":"","sources":["../../src/varsig.js"],"names":[],"mappings":"AAuDA;;GAEG;AACH,gCAFW,OAAO,YAAY,EAAE,aAAa,2BAc5C;AA2BD;;;;;;GAMG;AACH,4BAHW,UAAU,GACR,OAAO,YAAY,EAAE,YAAY,CAsD7C;AAvJD,qBAAsB,EAAI,CAAA;AAC1B,sBAAuB,CAAI,CAAA;AAC3B,8BAA+B,KAAM,CAAA;AAErC;;GAEG;AACH,4BAFU,MAAM,CAAC,MAAM,EAAE,OAAO,YAAY,EAAE,cAAc,CAAC,CAK5D;AAED;;GAEG;AACH,yBAFU,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAM9B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "iso-ucan",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"description": "Isomorphic UCAN",
|
|
6
|
+
"author": "Hugo Dias <hugomrdias@gmail.com> (hugodias.me)",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"url": "hugomrdias/iso-repo",
|
|
10
|
+
"directory": "packages/iso-ucan"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"ucan",
|
|
14
|
+
"varsig",
|
|
15
|
+
"crypto",
|
|
16
|
+
"isomorphic"
|
|
17
|
+
],
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/src/index.d.ts",
|
|
21
|
+
"default": "./src/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./capability": {
|
|
24
|
+
"types": "./dist/src/capability.d.ts",
|
|
25
|
+
"default": "./src/capability.js"
|
|
26
|
+
},
|
|
27
|
+
"./delegation": {
|
|
28
|
+
"types": "./dist/src/delegation.d.ts",
|
|
29
|
+
"default": "./src/delegation.js"
|
|
30
|
+
},
|
|
31
|
+
"./invocation": {
|
|
32
|
+
"types": "./dist/src/invocation.d.ts",
|
|
33
|
+
"default": "./src/invocation.js"
|
|
34
|
+
},
|
|
35
|
+
"./envelope": {
|
|
36
|
+
"types": "./dist/src/envelope.d.ts",
|
|
37
|
+
"default": "./src/envelope.js"
|
|
38
|
+
},
|
|
39
|
+
"./store": {
|
|
40
|
+
"types": "./dist/src/store.d.ts",
|
|
41
|
+
"default": "./src/store.js"
|
|
42
|
+
},
|
|
43
|
+
"./varsig": {
|
|
44
|
+
"types": "./dist/src/varsig.d.ts",
|
|
45
|
+
"default": "./src/varsig.js"
|
|
46
|
+
},
|
|
47
|
+
"./server": {
|
|
48
|
+
"types": "./dist/src/server.d.ts",
|
|
49
|
+
"default": "./src/server.js"
|
|
50
|
+
},
|
|
51
|
+
"./policy": {
|
|
52
|
+
"types": "./dist/src/policy.d.ts",
|
|
53
|
+
"default": "./src/policy.js"
|
|
54
|
+
},
|
|
55
|
+
"./client": {
|
|
56
|
+
"types": "./dist/src/client.d.ts",
|
|
57
|
+
"default": "./src/client.js"
|
|
58
|
+
},
|
|
59
|
+
"./types": {
|
|
60
|
+
"types": "./dist/src/types.d.ts"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"main": "src/index.js",
|
|
64
|
+
"types": "dist/src/index.d.ts",
|
|
65
|
+
"typesVersions": {
|
|
66
|
+
"*": {
|
|
67
|
+
"capability": [
|
|
68
|
+
"dist/src/capability"
|
|
69
|
+
],
|
|
70
|
+
"delegation": [
|
|
71
|
+
"dist/src/delegation"
|
|
72
|
+
],
|
|
73
|
+
"varsig": [
|
|
74
|
+
"dist/src/varsig"
|
|
75
|
+
],
|
|
76
|
+
"invocation": [
|
|
77
|
+
"dist/src/invocation"
|
|
78
|
+
],
|
|
79
|
+
"envelope": [
|
|
80
|
+
"dist/src/envelope"
|
|
81
|
+
],
|
|
82
|
+
"store": [
|
|
83
|
+
"dist/src/store"
|
|
84
|
+
],
|
|
85
|
+
"server": [
|
|
86
|
+
"dist/src/server"
|
|
87
|
+
],
|
|
88
|
+
"policy": [
|
|
89
|
+
"dist/src/policy"
|
|
90
|
+
],
|
|
91
|
+
"client": [
|
|
92
|
+
"dist/src/client"
|
|
93
|
+
],
|
|
94
|
+
"types": [
|
|
95
|
+
"dist/src/types"
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"files": [
|
|
100
|
+
"src",
|
|
101
|
+
"dist/src/*.d.ts",
|
|
102
|
+
"dist/src/*.d.ts.map"
|
|
103
|
+
],
|
|
104
|
+
"dependencies": {
|
|
105
|
+
"@ipld/dag-cbor": "^9.2.4",
|
|
106
|
+
"@standard-schema/spec": "^1.0.0",
|
|
107
|
+
"@standard-schema/utils": "^0.3.0",
|
|
108
|
+
"iobuffer": "^6.0.0",
|
|
109
|
+
"iso-kv": "^3.0.3",
|
|
110
|
+
"iso-web": "^1.1.1",
|
|
111
|
+
"it-merge": "^3.0.12",
|
|
112
|
+
"multiformats": "^13.3.7",
|
|
113
|
+
"iso-base": "^4.0.2",
|
|
114
|
+
"iso-did": "^1.8.0",
|
|
115
|
+
"iso-signatures": "^0.3.2"
|
|
116
|
+
},
|
|
117
|
+
"devDependencies": {
|
|
118
|
+
"@types/node": "^24.1.0",
|
|
119
|
+
"playwright-test": "^14.1.11",
|
|
120
|
+
"typescript": "5.8.3",
|
|
121
|
+
"zod": "^4.0.5"
|
|
122
|
+
},
|
|
123
|
+
"publishConfig": {
|
|
124
|
+
"provenance": true
|
|
125
|
+
},
|
|
126
|
+
"depcheck": {
|
|
127
|
+
"specials": [
|
|
128
|
+
"bin"
|
|
129
|
+
],
|
|
130
|
+
"ignores": [
|
|
131
|
+
"@types/*"
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
"scripts": {
|
|
135
|
+
"lint": "tsc --build && biome check --no-errors-on-unmatched --files-ignore-unknown=true .",
|
|
136
|
+
"test": "tsc --build && pnpm run test:node && pnpm run test:browser",
|
|
137
|
+
"test:node": "playwright-test 'test/**/!(*.browser).test.{js,ts}' --mode node",
|
|
138
|
+
"test:browser": "playwright-test 'test/**/!(*.node).test.{js,ts}'"
|
|
139
|
+
}
|
|
140
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# iso-ucan [](https://www.npmjs.com/package/iso-ucan) [](https://github.com/hugomrdias/iso-repo/blob/main/license) [](https://github.com/hugomrdias/iso-repo/actions/workflows/iso-ucan.yml)
|
|
2
|
+
|
|
3
|
+
> Isomorphic UCAN
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm install iso-ucan
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { Capability } from 'iso-ucan/capability'
|
|
15
|
+
import { Store } from 'iso-ucan/store'
|
|
16
|
+
import { MemoryDriver } from 'iso-kv/drivers/memory'
|
|
17
|
+
import { EdDSASigner } from 'iso-signatures/signers/eddsa.js'
|
|
18
|
+
import { z } from 'zod'
|
|
19
|
+
|
|
20
|
+
const store = new Store(new MemoryDriver())
|
|
21
|
+
|
|
22
|
+
const AccountCreateCap = Capability.from({
|
|
23
|
+
schema: z.object({
|
|
24
|
+
type: z.string(),
|
|
25
|
+
properties: z
|
|
26
|
+
.object({
|
|
27
|
+
name: z.string(),
|
|
28
|
+
})
|
|
29
|
+
.strict(),
|
|
30
|
+
}),
|
|
31
|
+
cmd: '/account/create',
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const AccountCap = Capability.from({
|
|
35
|
+
schema: z.never(),
|
|
36
|
+
cmd: '/account',
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const owner = await EdDSASigner.generate()
|
|
40
|
+
const bob = await EdDSASigner.generate()
|
|
41
|
+
const invoker = await EdDSASigner.generate()
|
|
42
|
+
|
|
43
|
+
const nowInSeconds = Math.floor(Date.now() / 1000)
|
|
44
|
+
|
|
45
|
+
const ownerDelegation = await AccountCap.delegate({
|
|
46
|
+
iss: owner,
|
|
47
|
+
aud: bob,
|
|
48
|
+
sub: owner,
|
|
49
|
+
pol: [],
|
|
50
|
+
exp: nowInSeconds + 1000,
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
await store.set(ownerDelegation)
|
|
54
|
+
|
|
55
|
+
const bobDelegation = await AccountCap.delegate({
|
|
56
|
+
iss: bob,
|
|
57
|
+
aud: invoker,
|
|
58
|
+
sub: owner,
|
|
59
|
+
pol: [],
|
|
60
|
+
exp: nowInSeconds + 1000,
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
await store.set(bobDelegation)
|
|
64
|
+
|
|
65
|
+
const invocation = await AccountCreateCap.invoke({
|
|
66
|
+
iss: invoker,
|
|
67
|
+
sub: owner,
|
|
68
|
+
args: {
|
|
69
|
+
type: 'account',
|
|
70
|
+
properties: {
|
|
71
|
+
name: 'John Doe',
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
store,
|
|
75
|
+
exp: nowInSeconds + 1000,
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
MIT © [Hugo Dias](http://hugodias.me)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SchemaError } from '@standard-schema/utils'
|
|
2
|
+
import { Delegation } from './delegation.js'
|
|
3
|
+
import { Invocation } from './invocation.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @import {StandardSchemaV1} from '@standard-schema/spec'
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @template {StandardSchemaV1} Schema
|
|
11
|
+
* @template {string} [Cmd=string]
|
|
12
|
+
* @typedef {Object} CapabilityOptions
|
|
13
|
+
* @prop {Schema} schema
|
|
14
|
+
* @prop {Cmd} cmd
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @template {StandardSchemaV1} Schema
|
|
19
|
+
* @template {string} [Cmd=string]
|
|
20
|
+
*/
|
|
21
|
+
export class Capability {
|
|
22
|
+
/**
|
|
23
|
+
* @param {CapabilityOptions<Schema, Cmd>} options
|
|
24
|
+
*/
|
|
25
|
+
constructor(options) {
|
|
26
|
+
this.schema = options.schema
|
|
27
|
+
this.cmd = options.cmd
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @template {StandardSchemaV1} Schema
|
|
32
|
+
* @template {string} [Cmd=string]
|
|
33
|
+
* @param {CapabilityOptions<Schema, Cmd>} options
|
|
34
|
+
*/
|
|
35
|
+
static from(options) {
|
|
36
|
+
return new Capability(options)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {StandardSchemaV1.InferInput<Schema>} args
|
|
41
|
+
*/
|
|
42
|
+
async validate(args) {
|
|
43
|
+
const result = await this.schema['~standard'].validate(args)
|
|
44
|
+
return result
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {import("./types.js").CapabilityInvokeOptions<Schema>} options
|
|
49
|
+
*/
|
|
50
|
+
async invoke(options) {
|
|
51
|
+
const result = await this.schema['~standard'].validate(options.args)
|
|
52
|
+
if (result.issues) {
|
|
53
|
+
throw new SchemaError(result.issues)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const proofs = await options.store.chain({
|
|
57
|
+
cmd: this.cmd,
|
|
58
|
+
sub: options.sub,
|
|
59
|
+
aud: options.iss.did,
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
return await Invocation.create({
|
|
63
|
+
...options,
|
|
64
|
+
args: result.value,
|
|
65
|
+
cmd: this.cmd,
|
|
66
|
+
prf: proofs,
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
*
|
|
72
|
+
* @param {import("./types.js").CapabilityDelegateOptions} options
|
|
73
|
+
*/
|
|
74
|
+
delegate(options) {
|
|
75
|
+
return Delegation.create({ ...options, cmd: this.cmd })
|
|
76
|
+
}
|
|
77
|
+
}
|
package/src/client.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import {InferProtocol, RouteOutput, StandardSchemaV1, ClientOptions, RouterClient} from './types'
|
|
3
|
+
* @import {Capability} from './capability'
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Workaround to make imports types work
|
|
8
|
+
* @template {{ cmd: string; in: unknown; out: unknown; }} T
|
|
9
|
+
* @typedef {Object} DELETE_ME
|
|
10
|
+
* @prop {string} url
|
|
11
|
+
* @prop {Capability<StandardSchemaV1, string>[]} capabilities
|
|
12
|
+
* @prop {InferProtocol<Record<string, RouteOutput<Capability<StandardSchemaV1>, unknown>>>} router
|
|
13
|
+
* @prop {ClientOptions} options
|
|
14
|
+
* @prop {RouterClient<T>} routerClient
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @template {InferProtocol<Record<string, RouteOutput<Capability<StandardSchemaV1>, unknown>>>} T
|
|
19
|
+
* @param {ClientOptions} options
|
|
20
|
+
* @returns {RouterClient<T>}
|
|
21
|
+
*/
|
|
22
|
+
export function createClient(options) {
|
|
23
|
+
const { url, capabilities } = options
|
|
24
|
+
|
|
25
|
+
// Create a map of capabilities by their `cmd` property
|
|
26
|
+
const capabilityMap = Object.fromEntries(
|
|
27
|
+
capabilities.map((cap) => [cap.cmd, cap])
|
|
28
|
+
)
|
|
29
|
+
return {
|
|
30
|
+
request: async (/** @type {string} */ cmd, /** @type {any} */ args) => {
|
|
31
|
+
const capability = capabilityMap[cmd]
|
|
32
|
+
|
|
33
|
+
if (!capability) {
|
|
34
|
+
throw new Error(`Capability not found: ${cmd}`)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const inv = await capability.invoke({
|
|
38
|
+
args,
|
|
39
|
+
iss: options.issuer,
|
|
40
|
+
sub: options.audience.did,
|
|
41
|
+
store: options.store,
|
|
42
|
+
exp: null,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const response = await fetch(url, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
body: inv.bytes,
|
|
48
|
+
})
|
|
49
|
+
return response.json()
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { base64pad } from 'iso-base/rfc4648'
|
|
2
|
+
import { parse as didParse } from 'iso-did'
|
|
3
|
+
import { randomBytes } from 'iso-web/crypto'
|
|
4
|
+
import * as Envelope from './envelope.js'
|
|
5
|
+
import {
|
|
6
|
+
assertExpiration,
|
|
7
|
+
assertIsValidCommand,
|
|
8
|
+
assertMeta,
|
|
9
|
+
assertNonce,
|
|
10
|
+
assertNotBefore,
|
|
11
|
+
assertNotRevoked,
|
|
12
|
+
cid,
|
|
13
|
+
expOrTtl,
|
|
14
|
+
verifySignature,
|
|
15
|
+
} from './utils.js'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @import {DelegationValidateOptions, Policy, DelegationOptions, DelegationPayload, DID, DIDURL} from './types.js'
|
|
19
|
+
* @import {CID} from 'multiformats'
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* UCAN Delegation
|
|
24
|
+
*/
|
|
25
|
+
export class Delegation {
|
|
26
|
+
/** @type {DIDURL} */
|
|
27
|
+
iss
|
|
28
|
+
/** @type {DID} */
|
|
29
|
+
aud
|
|
30
|
+
/** @type {DID | null} */
|
|
31
|
+
sub
|
|
32
|
+
/** @type {string} */
|
|
33
|
+
cmd
|
|
34
|
+
/** @type {Policy} */
|
|
35
|
+
pol
|
|
36
|
+
/** @type {number | undefined} */
|
|
37
|
+
nbf
|
|
38
|
+
/** @type {Uint8Array} */
|
|
39
|
+
nonce
|
|
40
|
+
/** @type {Record<string, unknown> | undefined} */
|
|
41
|
+
meta
|
|
42
|
+
/** @type {number | null} */
|
|
43
|
+
exp
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
*
|
|
47
|
+
* @param {import("./types.js").DecodedEnvelope<'dlg'>} envelope
|
|
48
|
+
* @param {Uint8Array} bytes
|
|
49
|
+
* @param {CID} cid
|
|
50
|
+
*/
|
|
51
|
+
constructor(envelope, bytes, cid) {
|
|
52
|
+
this.envelope = envelope
|
|
53
|
+
this.bytes = bytes
|
|
54
|
+
this.cid = cid
|
|
55
|
+
|
|
56
|
+
this.iss = envelope.payload.iss
|
|
57
|
+
this.aud = envelope.payload.aud
|
|
58
|
+
this.sub = envelope.payload.sub ? envelope.payload.sub : null
|
|
59
|
+
this.cmd = envelope.payload.cmd
|
|
60
|
+
this.pol = envelope.payload.pol
|
|
61
|
+
this.exp = envelope.payload.exp
|
|
62
|
+
this.nbf = envelope.payload.nbf
|
|
63
|
+
this.nonce = envelope.payload.nonce
|
|
64
|
+
this.meta = envelope.payload.meta
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
*
|
|
69
|
+
* @param {import("./types.js").DelegationFromOptions} options
|
|
70
|
+
*/
|
|
71
|
+
static async from(options) {
|
|
72
|
+
const { bytes, didResolver: didResolveOptions, verifierResolver } = options
|
|
73
|
+
|
|
74
|
+
const envelope = /** @type {typeof Envelope.decode<"dlg">} */ (
|
|
75
|
+
Envelope.decode
|
|
76
|
+
)({ envelope: bytes })
|
|
77
|
+
|
|
78
|
+
if (envelope.spec !== 'dlg') {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Invalid envelope type. Expected: Delegation but got: ${envelope.spec}`
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
assertStructure(envelope.payload)
|
|
85
|
+
|
|
86
|
+
await verifySignature(envelope, verifierResolver, didResolveOptions)
|
|
87
|
+
|
|
88
|
+
const _cid = await cid(envelope)
|
|
89
|
+
await assertNotRevoked(_cid, options.isRevoked)
|
|
90
|
+
|
|
91
|
+
return new Delegation(envelope, bytes, _cid)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
*
|
|
96
|
+
* @param {DelegationOptions & { cmd: string }} options
|
|
97
|
+
*/
|
|
98
|
+
static async create(options) {
|
|
99
|
+
const nonce = options.nonce || randomBytes(12)
|
|
100
|
+
|
|
101
|
+
/** @type {DelegationPayload} */
|
|
102
|
+
const payload = {
|
|
103
|
+
iss: options.iss.toString(),
|
|
104
|
+
aud: options.aud,
|
|
105
|
+
sub: options.sub ? options.sub : null,
|
|
106
|
+
pol: options.pol,
|
|
107
|
+
cmd: options.cmd,
|
|
108
|
+
nonce,
|
|
109
|
+
exp: expOrTtl(options),
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (options.nbf) {
|
|
113
|
+
payload.nbf = options.nbf
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (options.meta) {
|
|
117
|
+
payload.meta = options.meta
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
assertStructure(payload)
|
|
121
|
+
|
|
122
|
+
const { signature, signaturePayload } = await Envelope.sign({
|
|
123
|
+
spec: 'dlg',
|
|
124
|
+
signer: options.iss,
|
|
125
|
+
payload,
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const bytes = Envelope.encode({ signature, signaturePayload })
|
|
129
|
+
|
|
130
|
+
/** @type {import("./types.js").DecodedEnvelope<'dlg'>} */
|
|
131
|
+
const envelope = {
|
|
132
|
+
alg: options.iss.signatureType,
|
|
133
|
+
enc: 'DAG-CBOR',
|
|
134
|
+
signature,
|
|
135
|
+
payload,
|
|
136
|
+
spec: 'dlg',
|
|
137
|
+
version: Envelope.VERSION,
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return new Delegation(envelope, bytes, await cid(envelope))
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @param {DelegationValidateOptions} options
|
|
145
|
+
*/
|
|
146
|
+
async validate(options) {
|
|
147
|
+
assertStructure(this.envelope.payload)
|
|
148
|
+
assertNotBefore(this.envelope.payload.nbf)
|
|
149
|
+
await verifySignature(
|
|
150
|
+
this.envelope,
|
|
151
|
+
options.verifierResolver,
|
|
152
|
+
options.didResolver
|
|
153
|
+
)
|
|
154
|
+
await assertNotRevoked(this.cid, options.isRevoked)
|
|
155
|
+
|
|
156
|
+
return true
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
toString() {
|
|
160
|
+
return base64pad.encode(this.bytes)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
*
|
|
166
|
+
* @param {import('./types.js').DelegationPayload} payload
|
|
167
|
+
*/
|
|
168
|
+
function assertStructure(payload) {
|
|
169
|
+
didParse(payload.iss)
|
|
170
|
+
didParse(payload.aud)
|
|
171
|
+
|
|
172
|
+
if (payload.sub !== null) {
|
|
173
|
+
didParse(payload.sub)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
assertIsValidCommand(payload.cmd)
|
|
177
|
+
assertExpiration(payload.exp)
|
|
178
|
+
assertNonce(payload.nonce)
|
|
179
|
+
|
|
180
|
+
// assertPolicy(payload.pol)
|
|
181
|
+
|
|
182
|
+
assertMeta(payload.meta)
|
|
183
|
+
}
|