@aroha-sdk/credentials 1.0.0 → 1.2.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/dist/credentials.d.ts +53 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +117 -0
- package/dist/credentials.js.map +1 -0
- package/dist/credentials.test.d.ts +2 -0
- package/dist/credentials.test.d.ts.map +1 -0
- package/dist/credentials.test.js +69 -0
- package/dist/credentials.test.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/mandate.d.ts +59 -0
- package/dist/mandate.d.ts.map +1 -0
- package/dist/mandate.js +180 -0
- package/dist/mandate.js.map +1 -0
- package/dist/mandate.test.d.ts +2 -0
- package/dist/mandate.test.d.ts.map +1 -0
- package/dist/mandate.test.js +222 -0
- package/dist/mandate.test.js.map +1 -0
- package/dist/middleware.d.ts +53 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +227 -0
- package/dist/middleware.js.map +1 -0
- package/dist/rbac.d.ts +22 -0
- package/dist/rbac.d.ts.map +1 -0
- package/dist/rbac.js +50 -0
- package/dist/rbac.js.map +1 -0
- package/dist/rbac.test.d.ts +2 -0
- package/dist/rbac.test.d.ts.map +1 -0
- package/dist/rbac.test.js +97 -0
- package/dist/rbac.test.js.map +1 -0
- package/dist/registry.d.ts +125 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +227 -0
- package/dist/registry.js.map +1 -0
- package/dist/registry.test.d.ts +2 -0
- package/dist/registry.test.d.ts.map +1 -0
- package/dist/registry.test.js +142 -0
- package/dist/registry.test.js.map +1 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/package.json +5 -1
- package/tsconfig.json +0 -12
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { checkPermission, actionForMessageType } from "./rbac.js";
|
|
3
|
+
import { ArohaRole } from "./types.js";
|
|
4
|
+
describe("actionForMessageType", () => {
|
|
5
|
+
it("maps ArohaRequest to query", () => {
|
|
6
|
+
expect(actionForMessageType("ArohaRequest")).toBe("query");
|
|
7
|
+
});
|
|
8
|
+
it("maps ArohaReserve to reserve", () => {
|
|
9
|
+
expect(actionForMessageType("ArohaReserve")).toBe("reserve");
|
|
10
|
+
});
|
|
11
|
+
it("maps ArohaCommit to commit", () => {
|
|
12
|
+
expect(actionForMessageType("ArohaCommit")).toBe("commit");
|
|
13
|
+
});
|
|
14
|
+
it("maps ArohaCancel to cancel", () => {
|
|
15
|
+
expect(actionForMessageType("ArohaCancel")).toBe("cancel");
|
|
16
|
+
});
|
|
17
|
+
it("maps ArohaDelegate to delegate", () => {
|
|
18
|
+
expect(actionForMessageType("ArohaDelegate")).toBe("delegate");
|
|
19
|
+
});
|
|
20
|
+
it("maps ArohaNegotiate to negotiate", () => {
|
|
21
|
+
expect(actionForMessageType("ArohaNegotiate")).toBe("negotiate");
|
|
22
|
+
});
|
|
23
|
+
it("returns null for response/ack types", () => {
|
|
24
|
+
expect(actionForMessageType("ArohaResponse")).toBeNull();
|
|
25
|
+
expect(actionForMessageType("ArohaReserveAck")).toBeNull();
|
|
26
|
+
expect(actionForMessageType("ArohaCommitAck")).toBeNull();
|
|
27
|
+
expect(actionForMessageType("ArohaError")).toBeNull();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe("checkPermission", () => {
|
|
31
|
+
it("allows HumanAdmin all actions", () => {
|
|
32
|
+
const actions = ["query", "reserve", "commit", "cancel", "delegate", "negotiate", "admin"];
|
|
33
|
+
for (const action of actions) {
|
|
34
|
+
const result = checkPermission([ArohaRole.HumanAdmin], action);
|
|
35
|
+
expect(result.allowed).toBe(true);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
it("allows HumanUser to query/reserve/commit/cancel/negotiate but not delegate or admin", () => {
|
|
39
|
+
expect(checkPermission([ArohaRole.HumanUser], "query").allowed).toBe(true);
|
|
40
|
+
expect(checkPermission([ArohaRole.HumanUser], "reserve").allowed).toBe(true);
|
|
41
|
+
expect(checkPermission([ArohaRole.HumanUser], "delegate").allowed).toBe(false);
|
|
42
|
+
expect(checkPermission([ArohaRole.HumanUser], "admin").allowed).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
it("allows HumanReadonly only query", () => {
|
|
45
|
+
expect(checkPermission([ArohaRole.HumanReadonly], "query").allowed).toBe(true);
|
|
46
|
+
expect(checkPermission([ArohaRole.HumanReadonly], "reserve").allowed).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
it("denies AgentProvider all actions", () => {
|
|
49
|
+
expect(checkPermission([ArohaRole.AgentProvider], "query").allowed).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
it("allows AgentOrchestrator to delegate", () => {
|
|
52
|
+
expect(checkPermission([ArohaRole.AgentOrchestrator], "delegate").allowed).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
it("returns false with no roles", () => {
|
|
55
|
+
const result = checkPermission([], "query");
|
|
56
|
+
expect(result.allowed).toBe(false);
|
|
57
|
+
expect(result.reason).toMatch(/No roles/);
|
|
58
|
+
});
|
|
59
|
+
it("unions permissions across multiple roles", () => {
|
|
60
|
+
// HumanReadonly (query only) + AgentOrchestrator (query+reserve+..+delegate) → delegate allowed
|
|
61
|
+
const result = checkPermission([ArohaRole.HumanReadonly, ArohaRole.AgentOrchestrator], "delegate");
|
|
62
|
+
expect(result.allowed).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
it("includes role list in result", () => {
|
|
65
|
+
const result = checkPermission([ArohaRole.HumanUser], "query");
|
|
66
|
+
expect(result.roles).toContain(ArohaRole.HumanUser);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe("ArohaSpendingMandate action mapping", () => {
|
|
70
|
+
it("maps ArohaSpendingMandate to mandate", () => {
|
|
71
|
+
expect(actionForMessageType("ArohaSpendingMandate")).toBe("mandate");
|
|
72
|
+
});
|
|
73
|
+
it("ArohaSatisfactionSignal returns null (private signal, no RBAC)", () => {
|
|
74
|
+
expect(actionForMessageType("ArohaSatisfactionSignal")).toBeNull();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe("mandate action permissions", () => {
|
|
78
|
+
it("allows HumanAdmin to issue mandates", () => {
|
|
79
|
+
expect(checkPermission([ArohaRole.HumanAdmin], "mandate").allowed).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
it("allows HumanUser to issue mandates", () => {
|
|
82
|
+
expect(checkPermission([ArohaRole.HumanUser], "mandate").allowed).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
it("allows AgentOrchestrator to issue mandates", () => {
|
|
85
|
+
expect(checkPermission([ArohaRole.AgentOrchestrator], "mandate").allowed).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
it("denies HumanReadonly from issuing mandates", () => {
|
|
88
|
+
expect(checkPermission([ArohaRole.HumanReadonly], "mandate").allowed).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
it("denies AgentObserver from issuing mandates", () => {
|
|
91
|
+
expect(checkPermission([ArohaRole.AgentObserver], "mandate").allowed).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
it("denies AgentProvider from issuing mandates", () => {
|
|
94
|
+
expect(checkPermission([ArohaRole.AgentProvider], "mandate").allowed).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
//# sourceMappingURL=rbac.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rbac.test.js","sourceRoot":"","sources":["../src/rbac.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aroha Protocol — Human Credential Registry
|
|
3
|
+
*
|
|
4
|
+
* Stores signed HumanCredentials so that any agent in the network can
|
|
5
|
+
* verify a human user's identity and roles by credential ID, without
|
|
6
|
+
* requiring the full credential token to be re-sent on every message.
|
|
7
|
+
*
|
|
8
|
+
* Flow:
|
|
9
|
+
* 1. Personal Agent issues a HumanCredential (issueHumanCredential)
|
|
10
|
+
* 2. Personal Agent registers it in the registry (CredentialRegistry.register)
|
|
11
|
+
* 3. Human sends a Aroha message with credentialId in the body
|
|
12
|
+
* 4. Provider agent looks up the credential (CredentialRegistry.resolve)
|
|
13
|
+
* 5. Provider verifies the stored credential's signature and checks roles
|
|
14
|
+
*
|
|
15
|
+
* The registry is exposed as HTTP endpoints by ArohaServer when
|
|
16
|
+
* ArohaServerOptions.credentialRegistry is set:
|
|
17
|
+
*
|
|
18
|
+
* POST /aroha/credentials — register (verifies sig before storing)
|
|
19
|
+
* GET /aroha/credentials/:id — resolve by ID (public)
|
|
20
|
+
* DELETE /aroha/credentials/:id — revoke (requires issuer proof header)
|
|
21
|
+
*
|
|
22
|
+
* Remote agents use CredentialRegistryClient to call these endpoints.
|
|
23
|
+
*/
|
|
24
|
+
import { type HumanCredential } from "./types.js";
|
|
25
|
+
export interface CredentialStore {
|
|
26
|
+
set(id: string, credential: HumanCredential): Promise<void> | void;
|
|
27
|
+
get(id: string): Promise<HumanCredential | undefined> | HumanCredential | undefined;
|
|
28
|
+
delete(id: string): Promise<boolean> | boolean;
|
|
29
|
+
values(): AsyncIterable<HumanCredential> | Iterable<HumanCredential>;
|
|
30
|
+
}
|
|
31
|
+
export declare class MapCredentialStore implements CredentialStore {
|
|
32
|
+
private readonly store;
|
|
33
|
+
set(id: string, credential: HumanCredential): void;
|
|
34
|
+
get(id: string): HumanCredential | undefined;
|
|
35
|
+
delete(id: string): boolean;
|
|
36
|
+
values(): Iterable<HumanCredential>;
|
|
37
|
+
get size(): number;
|
|
38
|
+
}
|
|
39
|
+
export declare class CredentialRegistry {
|
|
40
|
+
private readonly store;
|
|
41
|
+
constructor(store?: CredentialStore);
|
|
42
|
+
/**
|
|
43
|
+
* Register a credential after verifying its Ed25519 signature.
|
|
44
|
+
*
|
|
45
|
+
* @param credential The credential to store
|
|
46
|
+
* @param issuerPublicKey Ed25519 public key of credential.issuerDID (32 bytes)
|
|
47
|
+
* @returns `true` if stored successfully, `false` if signature is invalid
|
|
48
|
+
*/
|
|
49
|
+
register(credential: HumanCredential, issuerPublicKey: Uint8Array): Promise<boolean>;
|
|
50
|
+
/**
|
|
51
|
+
* Register a credential that has already been verified.
|
|
52
|
+
* Use this when the calling code has already confirmed validity
|
|
53
|
+
* (e.g., the issuing agent registering its own credential).
|
|
54
|
+
*/
|
|
55
|
+
registerTrusted(credential: HumanCredential): void;
|
|
56
|
+
/**
|
|
57
|
+
* Resolve a credential by ID.
|
|
58
|
+
* Returns null if not found, or if the credential has expired (auto-evicts).
|
|
59
|
+
*/
|
|
60
|
+
resolve(credentialId: string): HumanCredential | null;
|
|
61
|
+
/**
|
|
62
|
+
* Revoke a credential by ID.
|
|
63
|
+
* @returns `true` if it existed and was removed, `false` if not found
|
|
64
|
+
*/
|
|
65
|
+
revoke(credentialId: string): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* List all active (non-expired) credentials for a given userId.
|
|
68
|
+
*/
|
|
69
|
+
listForUser(userId: string): HumanCredential[];
|
|
70
|
+
/** Total number of stored credentials (including potentially expired ones). */
|
|
71
|
+
get size(): number;
|
|
72
|
+
/**
|
|
73
|
+
* Evict all expired credentials. Called periodically to free memory.
|
|
74
|
+
* Returns the number of credentials evicted.
|
|
75
|
+
*/
|
|
76
|
+
evictExpired(): number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* HTTP client for remote credential registry lookups.
|
|
80
|
+
*
|
|
81
|
+
* Any agent can use this to query the registry hosted by a Personal Agent
|
|
82
|
+
* (or any ArohaServer with credentialRegistry configured).
|
|
83
|
+
*/
|
|
84
|
+
export declare class CredentialRegistryClient {
|
|
85
|
+
private readonly baseUrl;
|
|
86
|
+
constructor(baseUrl: string);
|
|
87
|
+
/**
|
|
88
|
+
* Register a credential in the remote registry.
|
|
89
|
+
* The registry will verify the credential's signature before storing.
|
|
90
|
+
*/
|
|
91
|
+
register(credential: HumanCredential): Promise<{
|
|
92
|
+
ok: boolean;
|
|
93
|
+
reason?: string;
|
|
94
|
+
}>;
|
|
95
|
+
/**
|
|
96
|
+
* Resolve a credential by ID from the remote registry.
|
|
97
|
+
* Returns null if not found or expired.
|
|
98
|
+
*/
|
|
99
|
+
resolve(credentialId: string): Promise<HumanCredential | null>;
|
|
100
|
+
/**
|
|
101
|
+
* Revoke a credential.
|
|
102
|
+
*
|
|
103
|
+
* The caller must provide a revocation proof: a base64url Ed25519 signature
|
|
104
|
+
* over `{credentialId, action:"revoke"}` using the issuer's private key.
|
|
105
|
+
* This is checked server-side before deletion.
|
|
106
|
+
*/
|
|
107
|
+
revoke(credentialId: string, revokeProof: string): Promise<{
|
|
108
|
+
ok: boolean;
|
|
109
|
+
reason?: string;
|
|
110
|
+
}>;
|
|
111
|
+
/**
|
|
112
|
+
* List all active credentials for a userId.
|
|
113
|
+
*/
|
|
114
|
+
listForUser(userId: string): Promise<HumanCredential[]>;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Build a revocation proof to pass to CredentialRegistryClient.revoke().
|
|
118
|
+
* Signs `{action:"revoke",credentialId}` with the issuer's private key.
|
|
119
|
+
*/
|
|
120
|
+
export declare function buildRevokeProof(credentialId: string, issuerPrivateKey: Uint8Array): Promise<string>;
|
|
121
|
+
/**
|
|
122
|
+
* Verify a revocation proof server-side.
|
|
123
|
+
*/
|
|
124
|
+
export declare function verifyRevokeProof(credentialId: string, proof: string, issuerPublicKey: Uint8Array): Promise<boolean>;
|
|
125
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAOlD,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACnE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,GAAG,eAAe,GAAG,SAAS,CAAC;IACpF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC/C,MAAM,IAAI,aAAa,CAAC,eAAe,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;CACtE;AAID,qBAAa,kBAAmB,YAAW,eAAe;IACxD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsC;IAE5D,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,eAAe,GAAG,IAAI;IAIlD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAI5C,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI3B,MAAM,IAAI,QAAQ,CAAC,eAAe,CAAC;IAInC,IAAI,IAAI,IAAI,MAAM,CAA4B;CAC/C;AAID,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;gBAE5B,KAAK,GAAE,eAA0C;IAI7D;;;;;;OAMG;IACG,QAAQ,CAAC,UAAU,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;IAS1F;;;;OAIG;IACH,eAAe,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI;IAIlD;;;OAGG;IACH,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAYrD;;;OAGG;IACH,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAMrC;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,EAAE;IAY9C,+EAA+E;IAC/E,IAAI,IAAI,IAAI,MAAM,CAGjB;IAED;;;OAGG;IACH,YAAY,IAAI,MAAM;CAYvB;AAID;;;;;GAKG;AACH,qBAAa,wBAAwB;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,MAAM;IAE5C;;;OAGG;IACG,QAAQ,CAAC,UAAU,EAAE,eAAe,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAWtF;;;OAGG;IACG,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAMpE;;;;;;OAMG;IACG,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAUlG;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;CAK9D;AAID;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,UAAU,GAC3B,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,UAAU,GAC1B,OAAO,CAAC,OAAO,CAAC,CASlB"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aroha Protocol — Human Credential Registry
|
|
3
|
+
*
|
|
4
|
+
* Stores signed HumanCredentials so that any agent in the network can
|
|
5
|
+
* verify a human user's identity and roles by credential ID, without
|
|
6
|
+
* requiring the full credential token to be re-sent on every message.
|
|
7
|
+
*
|
|
8
|
+
* Flow:
|
|
9
|
+
* 1. Personal Agent issues a HumanCredential (issueHumanCredential)
|
|
10
|
+
* 2. Personal Agent registers it in the registry (CredentialRegistry.register)
|
|
11
|
+
* 3. Human sends a Aroha message with credentialId in the body
|
|
12
|
+
* 4. Provider agent looks up the credential (CredentialRegistry.resolve)
|
|
13
|
+
* 5. Provider verifies the stored credential's signature and checks roles
|
|
14
|
+
*
|
|
15
|
+
* The registry is exposed as HTTP endpoints by ArohaServer when
|
|
16
|
+
* ArohaServerOptions.credentialRegistry is set:
|
|
17
|
+
*
|
|
18
|
+
* POST /aroha/credentials — register (verifies sig before storing)
|
|
19
|
+
* GET /aroha/credentials/:id — resolve by ID (public)
|
|
20
|
+
* DELETE /aroha/credentials/:id — revoke (requires issuer proof header)
|
|
21
|
+
*
|
|
22
|
+
* Remote agents use CredentialRegistryClient to call these endpoints.
|
|
23
|
+
*/
|
|
24
|
+
import * as ed from "@noble/ed25519";
|
|
25
|
+
import { sha512 } from "@noble/hashes/sha512";
|
|
26
|
+
import { verifyHumanCredential } from "./credentials.js";
|
|
27
|
+
ed.etc.sha512Sync = (...m) => sha512(...m);
|
|
28
|
+
// ─── Default in-process store ─────────────────────────────────────────────────
|
|
29
|
+
export class MapCredentialStore {
|
|
30
|
+
store = new Map();
|
|
31
|
+
set(id, credential) {
|
|
32
|
+
this.store.set(id, credential);
|
|
33
|
+
}
|
|
34
|
+
get(id) {
|
|
35
|
+
return this.store.get(id);
|
|
36
|
+
}
|
|
37
|
+
delete(id) {
|
|
38
|
+
return this.store.delete(id);
|
|
39
|
+
}
|
|
40
|
+
values() {
|
|
41
|
+
return this.store.values();
|
|
42
|
+
}
|
|
43
|
+
get size() { return this.store.size; }
|
|
44
|
+
}
|
|
45
|
+
// ─── Registry ─────────────────────────────────────────────────────────────────
|
|
46
|
+
export class CredentialRegistry {
|
|
47
|
+
store;
|
|
48
|
+
constructor(store = new MapCredentialStore()) {
|
|
49
|
+
this.store = store;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Register a credential after verifying its Ed25519 signature.
|
|
53
|
+
*
|
|
54
|
+
* @param credential The credential to store
|
|
55
|
+
* @param issuerPublicKey Ed25519 public key of credential.issuerDID (32 bytes)
|
|
56
|
+
* @returns `true` if stored successfully, `false` if signature is invalid
|
|
57
|
+
*/
|
|
58
|
+
async register(credential, issuerPublicKey) {
|
|
59
|
+
const { serializeCredential } = await import("./credentials.js");
|
|
60
|
+
const token = serializeCredential(credential);
|
|
61
|
+
const result = await verifyHumanCredential(token, issuerPublicKey);
|
|
62
|
+
if (!result.valid)
|
|
63
|
+
return false;
|
|
64
|
+
await this.store.set(credential.credentialId, credential);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Register a credential that has already been verified.
|
|
69
|
+
* Use this when the calling code has already confirmed validity
|
|
70
|
+
* (e.g., the issuing agent registering its own credential).
|
|
71
|
+
*/
|
|
72
|
+
registerTrusted(credential) {
|
|
73
|
+
this.store.set(credential.credentialId, credential);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Resolve a credential by ID.
|
|
77
|
+
* Returns null if not found, or if the credential has expired (auto-evicts).
|
|
78
|
+
*/
|
|
79
|
+
resolve(credentialId) {
|
|
80
|
+
const cred = this.store.get(credentialId);
|
|
81
|
+
if (!cred)
|
|
82
|
+
return null;
|
|
83
|
+
if (cred instanceof Promise)
|
|
84
|
+
return null; // async store: use async resolveAsync instead
|
|
85
|
+
const credTyped = cred;
|
|
86
|
+
if (new Date(credTyped.expiresAt) < new Date()) {
|
|
87
|
+
this.store.delete(credentialId); // evict expired
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return credTyped;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Revoke a credential by ID.
|
|
94
|
+
* @returns `true` if it existed and was removed, `false` if not found
|
|
95
|
+
*/
|
|
96
|
+
revoke(credentialId) {
|
|
97
|
+
const result = this.store.delete(credentialId);
|
|
98
|
+
if (result instanceof Promise)
|
|
99
|
+
return false;
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* List all active (non-expired) credentials for a given userId.
|
|
104
|
+
*/
|
|
105
|
+
listForUser(userId) {
|
|
106
|
+
const now = new Date();
|
|
107
|
+
const results = [];
|
|
108
|
+
const vals = this.store.values();
|
|
109
|
+
for (const cred of vals) {
|
|
110
|
+
if (cred.userId === userId && new Date(cred.expiresAt) >= now) {
|
|
111
|
+
results.push(cred);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return results;
|
|
115
|
+
}
|
|
116
|
+
/** Total number of stored credentials (including potentially expired ones). */
|
|
117
|
+
get size() {
|
|
118
|
+
const s = this.store;
|
|
119
|
+
return s.size ?? 0;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Evict all expired credentials. Called periodically to free memory.
|
|
123
|
+
* Returns the number of credentials evicted.
|
|
124
|
+
*/
|
|
125
|
+
evictExpired() {
|
|
126
|
+
const now = new Date();
|
|
127
|
+
let count = 0;
|
|
128
|
+
const vals = this.store.values();
|
|
129
|
+
for (const cred of vals) {
|
|
130
|
+
if (new Date(cred.expiresAt) < now) {
|
|
131
|
+
this.store.delete(cred.credentialId);
|
|
132
|
+
count++;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return count;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// ─── HTTP Client ───────────────────────────────────────────────────────────────
|
|
139
|
+
/**
|
|
140
|
+
* HTTP client for remote credential registry lookups.
|
|
141
|
+
*
|
|
142
|
+
* Any agent can use this to query the registry hosted by a Personal Agent
|
|
143
|
+
* (or any ArohaServer with credentialRegistry configured).
|
|
144
|
+
*/
|
|
145
|
+
export class CredentialRegistryClient {
|
|
146
|
+
baseUrl;
|
|
147
|
+
constructor(baseUrl) {
|
|
148
|
+
this.baseUrl = baseUrl;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Register a credential in the remote registry.
|
|
152
|
+
* The registry will verify the credential's signature before storing.
|
|
153
|
+
*/
|
|
154
|
+
async register(credential) {
|
|
155
|
+
const res = await fetch(`${this.baseUrl}/aroha/credentials`, {
|
|
156
|
+
method: "POST",
|
|
157
|
+
headers: { "Content-Type": "application/json" },
|
|
158
|
+
body: JSON.stringify(credential),
|
|
159
|
+
});
|
|
160
|
+
if (res.ok)
|
|
161
|
+
return { ok: true };
|
|
162
|
+
const body = await res.json().catch(() => ({}));
|
|
163
|
+
return { ok: false, reason: String(body.error ?? res.statusText) };
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Resolve a credential by ID from the remote registry.
|
|
167
|
+
* Returns null if not found or expired.
|
|
168
|
+
*/
|
|
169
|
+
async resolve(credentialId) {
|
|
170
|
+
const res = await fetch(`${this.baseUrl}/aroha/credentials/${encodeURIComponent(credentialId)}`);
|
|
171
|
+
if (!res.ok)
|
|
172
|
+
return null;
|
|
173
|
+
return res.json();
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Revoke a credential.
|
|
177
|
+
*
|
|
178
|
+
* The caller must provide a revocation proof: a base64url Ed25519 signature
|
|
179
|
+
* over `{credentialId, action:"revoke"}` using the issuer's private key.
|
|
180
|
+
* This is checked server-side before deletion.
|
|
181
|
+
*/
|
|
182
|
+
async revoke(credentialId, revokeProof) {
|
|
183
|
+
const res = await fetch(`${this.baseUrl}/aroha/credentials/${encodeURIComponent(credentialId)}`, {
|
|
184
|
+
method: "DELETE",
|
|
185
|
+
headers: { "X-Revoke-Proof": revokeProof },
|
|
186
|
+
});
|
|
187
|
+
if (res.ok)
|
|
188
|
+
return { ok: true };
|
|
189
|
+
const body = await res.json().catch(() => ({}));
|
|
190
|
+
return { ok: false, reason: String(body.error ?? res.statusText) };
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* List all active credentials for a userId.
|
|
194
|
+
*/
|
|
195
|
+
async listForUser(userId) {
|
|
196
|
+
const res = await fetch(`${this.baseUrl}/aroha/credentials?userId=${encodeURIComponent(userId)}`);
|
|
197
|
+
if (!res.ok)
|
|
198
|
+
return [];
|
|
199
|
+
return res.json();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// ─── Revoke proof helpers ──────────────────────────────────────────────────────
|
|
203
|
+
/**
|
|
204
|
+
* Build a revocation proof to pass to CredentialRegistryClient.revoke().
|
|
205
|
+
* Signs `{action:"revoke",credentialId}` with the issuer's private key.
|
|
206
|
+
*/
|
|
207
|
+
export async function buildRevokeProof(credentialId, issuerPrivateKey) {
|
|
208
|
+
const payload = JSON.stringify({ action: "revoke", credentialId });
|
|
209
|
+
const bytes = new TextEncoder().encode(payload);
|
|
210
|
+
const sig = await ed.signAsync(bytes, issuerPrivateKey);
|
|
211
|
+
return Buffer.from(sig).toString("base64url");
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Verify a revocation proof server-side.
|
|
215
|
+
*/
|
|
216
|
+
export async function verifyRevokeProof(credentialId, proof, issuerPublicKey) {
|
|
217
|
+
try {
|
|
218
|
+
const payload = JSON.stringify({ action: "revoke", credentialId });
|
|
219
|
+
const bytes = new TextEncoder().encode(payload);
|
|
220
|
+
const sig = Buffer.from(proof, "base64url");
|
|
221
|
+
return await ed.verifyAsync(sig, bytes, issuerPublicKey);
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.test.d.ts","sourceRoot":"","sources":["../src/registry.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import * as ed from "@noble/ed25519";
|
|
3
|
+
import { sha512 } from "@noble/hashes/sha512";
|
|
4
|
+
import { CredentialRegistry, buildRevokeProof, verifyRevokeProof } from "./registry.js";
|
|
5
|
+
import { issueHumanCredential } from "./credentials.js";
|
|
6
|
+
import { ArohaRole } from "./types.js";
|
|
7
|
+
ed.etc.sha512Sync = (...m) => sha512(...m);
|
|
8
|
+
async function makeIssuer() {
|
|
9
|
+
const priv = ed.utils.randomPrivateKey();
|
|
10
|
+
const pub = await ed.getPublicKeyAsync(priv);
|
|
11
|
+
return { priv, pub, did: "did:aroha:issuer" };
|
|
12
|
+
}
|
|
13
|
+
async function makeCred(issuer, userId = "user-1") {
|
|
14
|
+
return issueHumanCredential(userId, [ArohaRole.HumanUser], issuer.did, issuer.priv);
|
|
15
|
+
}
|
|
16
|
+
describe("CredentialRegistry", () => {
|
|
17
|
+
it("registers and resolves a valid credential", async () => {
|
|
18
|
+
const issuer = await makeIssuer();
|
|
19
|
+
const cred = await makeCred(issuer);
|
|
20
|
+
const registry = new CredentialRegistry();
|
|
21
|
+
const ok = await registry.register(cred, issuer.pub);
|
|
22
|
+
expect(ok).toBe(true);
|
|
23
|
+
const resolved = registry.resolve(cred.credentialId);
|
|
24
|
+
expect(resolved).not.toBeNull();
|
|
25
|
+
expect(resolved?.userId).toBe("user-1");
|
|
26
|
+
});
|
|
27
|
+
it("rejects registration with wrong issuer key", async () => {
|
|
28
|
+
const issuer = await makeIssuer();
|
|
29
|
+
const wrongIssuer = await makeIssuer();
|
|
30
|
+
const cred = await makeCred(issuer);
|
|
31
|
+
const registry = new CredentialRegistry();
|
|
32
|
+
const ok = await registry.register(cred, wrongIssuer.pub);
|
|
33
|
+
expect(ok).toBe(false);
|
|
34
|
+
expect(registry.resolve(cred.credentialId)).toBeNull();
|
|
35
|
+
});
|
|
36
|
+
it("registerTrusted bypasses signature check", async () => {
|
|
37
|
+
const issuer = await makeIssuer();
|
|
38
|
+
const cred = await makeCred(issuer);
|
|
39
|
+
const registry = new CredentialRegistry();
|
|
40
|
+
registry.registerTrusted(cred);
|
|
41
|
+
expect(registry.resolve(cred.credentialId)).not.toBeNull();
|
|
42
|
+
});
|
|
43
|
+
it("revokes a credential", async () => {
|
|
44
|
+
const issuer = await makeIssuer();
|
|
45
|
+
const cred = await makeCred(issuer);
|
|
46
|
+
const registry = new CredentialRegistry();
|
|
47
|
+
registry.registerTrusted(cred);
|
|
48
|
+
expect(registry.resolve(cred.credentialId)).not.toBeNull();
|
|
49
|
+
const removed = registry.revoke(cred.credentialId);
|
|
50
|
+
expect(removed).toBe(true);
|
|
51
|
+
expect(registry.resolve(cred.credentialId)).toBeNull();
|
|
52
|
+
});
|
|
53
|
+
it("returns false when revoking a non-existent credential", () => {
|
|
54
|
+
const registry = new CredentialRegistry();
|
|
55
|
+
expect(registry.revoke("nonexistent")).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
it("listForUser returns credentials for that user", async () => {
|
|
58
|
+
const issuer = await makeIssuer();
|
|
59
|
+
const cred1 = await makeCred(issuer, "alice");
|
|
60
|
+
const cred2 = await makeCred(issuer, "bob");
|
|
61
|
+
const registry = new CredentialRegistry();
|
|
62
|
+
registry.registerTrusted(cred1);
|
|
63
|
+
registry.registerTrusted(cred2);
|
|
64
|
+
const aliceList = registry.listForUser("alice");
|
|
65
|
+
expect(aliceList).toHaveLength(1);
|
|
66
|
+
expect(aliceList[0].userId).toBe("alice");
|
|
67
|
+
});
|
|
68
|
+
it("evicts expired credentials", async () => {
|
|
69
|
+
const issuer = await makeIssuer();
|
|
70
|
+
const expired = await issueHumanCredential("user-exp", [ArohaRole.HumanUser], issuer.did, issuer.priv, -1000);
|
|
71
|
+
const registry = new CredentialRegistry();
|
|
72
|
+
registry.registerTrusted(expired);
|
|
73
|
+
const count = registry.evictExpired();
|
|
74
|
+
expect(count).toBe(1);
|
|
75
|
+
expect(registry.size).toBe(0);
|
|
76
|
+
});
|
|
77
|
+
it("returns null on resolve of expired credential (auto-evict)", async () => {
|
|
78
|
+
const issuer = await makeIssuer();
|
|
79
|
+
const expired = await issueHumanCredential("user-exp", [ArohaRole.HumanUser], issuer.did, issuer.priv, -1000);
|
|
80
|
+
const registry = new CredentialRegistry();
|
|
81
|
+
registry.registerTrusted(expired);
|
|
82
|
+
expect(registry.resolve(expired.credentialId)).toBeNull();
|
|
83
|
+
expect(registry.size).toBe(0);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe("buildRevokeProof / verifyRevokeProof", () => {
|
|
87
|
+
it("builds a proof that verifies", async () => {
|
|
88
|
+
const issuer = await makeIssuer();
|
|
89
|
+
const proof = await buildRevokeProof("cred-123", issuer.priv);
|
|
90
|
+
const valid = await verifyRevokeProof("cred-123", proof, issuer.pub);
|
|
91
|
+
expect(valid).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
it("fails verification with wrong public key", async () => {
|
|
94
|
+
const issuer = await makeIssuer();
|
|
95
|
+
const other = await makeIssuer();
|
|
96
|
+
const proof = await buildRevokeProof("cred-456", issuer.priv);
|
|
97
|
+
const valid = await verifyRevokeProof("cred-456", proof, other.pub);
|
|
98
|
+
expect(valid).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
it("fails verification with different credentialId", async () => {
|
|
101
|
+
const issuer = await makeIssuer();
|
|
102
|
+
const proof = await buildRevokeProof("cred-A", issuer.priv);
|
|
103
|
+
const valid = await verifyRevokeProof("cred-B", proof, issuer.pub);
|
|
104
|
+
expect(valid).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
describe("CredentialStore interface", () => {
|
|
108
|
+
it("MapCredentialStore is exported and usable", async () => {
|
|
109
|
+
const { MapCredentialStore } = await import("./registry.js");
|
|
110
|
+
const store = new MapCredentialStore();
|
|
111
|
+
const cred = {
|
|
112
|
+
credentialId: "test-id",
|
|
113
|
+
userId: "user-1",
|
|
114
|
+
roles: [ArohaRole.HumanUser],
|
|
115
|
+
issuedAt: new Date().toISOString(),
|
|
116
|
+
expiresAt: new Date(Date.now() + 3600_000).toISOString(),
|
|
117
|
+
issuerDID: "did:aroha:issuer",
|
|
118
|
+
signature: "dummy",
|
|
119
|
+
};
|
|
120
|
+
await store.set(cred.credentialId, cred);
|
|
121
|
+
expect(await store.get(cred.credentialId)).toEqual(cred);
|
|
122
|
+
await store.delete(cred.credentialId);
|
|
123
|
+
expect(await store.get(cred.credentialId)).toBeUndefined();
|
|
124
|
+
});
|
|
125
|
+
it("CredentialRegistry accepts a custom store", async () => {
|
|
126
|
+
const { MapCredentialStore, CredentialRegistry } = await import("./registry.js");
|
|
127
|
+
const store = new MapCredentialStore();
|
|
128
|
+
const reg = new CredentialRegistry(store);
|
|
129
|
+
const cred = {
|
|
130
|
+
credentialId: "custom-store-id",
|
|
131
|
+
userId: "user-2",
|
|
132
|
+
roles: [ArohaRole.HumanUser],
|
|
133
|
+
issuedAt: new Date().toISOString(),
|
|
134
|
+
expiresAt: new Date(Date.now() + 3600_000).toISOString(),
|
|
135
|
+
issuerDID: "did:aroha:issuer",
|
|
136
|
+
signature: "dummy",
|
|
137
|
+
};
|
|
138
|
+
reg.registerTrusted(cred);
|
|
139
|
+
expect(reg.resolve("custom-store-id")).toEqual(cred);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
//# sourceMappingURL=registry.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.test.js","sourceRoot":"","sources":["../src/registry.test.ts"],"names":[],"mappings":"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"}
|