@kya-os/mcp-i-core 1.2.3-canary.6 → 1.3.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/.claude/settings.local.json +9 -0
- package/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-test$colon$coverage.log +4514 -0
- package/.turbo/turbo-test.log +2973 -0
- package/COMPLIANCE_IMPROVEMENT_REPORT.md +483 -0
- package/Composer 3.md +615 -0
- package/GPT-5.md +1169 -0
- package/OPUS-plan.md +352 -0
- package/PHASE_3_AND_4.1_SUMMARY.md +585 -0
- package/PHASE_3_SUMMARY.md +317 -0
- package/PHASE_4.1.3_SUMMARY.md +428 -0
- package/PHASE_4.1_COMPLETE.md +525 -0
- package/PHASE_4_USER_DID_IDENTITY_LINKING_PLAN.md +1240 -0
- package/SCHEMA_COMPLIANCE_REPORT.md +275 -0
- package/TEST_PLAN.md +571 -0
- package/coverage/coverage-final.json +57 -0
- package/dist/__tests__/utils/mock-providers.d.ts +1 -2
- package/dist/__tests__/utils/mock-providers.d.ts.map +1 -1
- package/dist/__tests__/utils/mock-providers.js.map +1 -1
- package/dist/cache/oauth-config-cache.d.ts +69 -0
- package/dist/cache/oauth-config-cache.d.ts.map +1 -0
- package/dist/cache/oauth-config-cache.js +76 -0
- package/dist/cache/oauth-config-cache.js.map +1 -0
- package/dist/identity/idp-token-resolver.d.ts +53 -0
- package/dist/identity/idp-token-resolver.d.ts.map +1 -0
- package/dist/identity/idp-token-resolver.js +108 -0
- package/dist/identity/idp-token-resolver.js.map +1 -0
- package/dist/identity/idp-token-storage.interface.d.ts +42 -0
- package/dist/identity/idp-token-storage.interface.d.ts.map +1 -0
- package/dist/identity/idp-token-storage.interface.js +12 -0
- package/dist/identity/idp-token-storage.interface.js.map +1 -0
- package/dist/identity/user-did-manager.d.ts +39 -1
- package/dist/identity/user-did-manager.d.ts.map +1 -1
- package/dist/identity/user-did-manager.js +69 -3
- package/dist/identity/user-did-manager.js.map +1 -1
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/audit-logger.d.ts +37 -0
- package/dist/runtime/audit-logger.d.ts.map +1 -0
- package/dist/runtime/audit-logger.js +9 -0
- package/dist/runtime/audit-logger.js.map +1 -0
- package/dist/runtime/base.d.ts +58 -2
- package/dist/runtime/base.d.ts.map +1 -1
- package/dist/runtime/base.js +266 -11
- package/dist/runtime/base.js.map +1 -1
- package/dist/services/access-control.service.d.ts.map +1 -1
- package/dist/services/access-control.service.js +200 -35
- package/dist/services/access-control.service.js.map +1 -1
- package/dist/services/authorization/authorization-registry.d.ts +29 -0
- package/dist/services/authorization/authorization-registry.d.ts.map +1 -0
- package/dist/services/authorization/authorization-registry.js +57 -0
- package/dist/services/authorization/authorization-registry.js.map +1 -0
- package/dist/services/authorization/types.d.ts +53 -0
- package/dist/services/authorization/types.d.ts.map +1 -0
- package/dist/services/authorization/types.js +10 -0
- package/dist/services/authorization/types.js.map +1 -0
- package/dist/services/batch-delegation.service.d.ts +53 -0
- package/dist/services/batch-delegation.service.d.ts.map +1 -0
- package/dist/services/batch-delegation.service.js +95 -0
- package/dist/services/batch-delegation.service.js.map +1 -0
- package/dist/services/oauth-config.service.d.ts +53 -0
- package/dist/services/oauth-config.service.d.ts.map +1 -0
- package/dist/services/oauth-config.service.js +117 -0
- package/dist/services/oauth-config.service.js.map +1 -0
- package/dist/services/oauth-provider-registry.d.ts +77 -0
- package/dist/services/oauth-provider-registry.d.ts.map +1 -0
- package/dist/services/oauth-provider-registry.js +112 -0
- package/dist/services/oauth-provider-registry.js.map +1 -0
- package/dist/services/oauth-service.d.ts +77 -0
- package/dist/services/oauth-service.d.ts.map +1 -0
- package/dist/services/oauth-service.js +348 -0
- package/dist/services/oauth-service.js.map +1 -0
- package/dist/services/oauth-token-retrieval.service.d.ts +49 -0
- package/dist/services/oauth-token-retrieval.service.d.ts.map +1 -0
- package/dist/services/oauth-token-retrieval.service.js +150 -0
- package/dist/services/oauth-token-retrieval.service.js.map +1 -0
- package/dist/services/provider-resolver.d.ts +48 -0
- package/dist/services/provider-resolver.d.ts.map +1 -0
- package/dist/services/provider-resolver.js +120 -0
- package/dist/services/provider-resolver.js.map +1 -0
- package/dist/services/provider-validator.d.ts +55 -0
- package/dist/services/provider-validator.d.ts.map +1 -0
- package/dist/services/provider-validator.js +135 -0
- package/dist/services/provider-validator.js.map +1 -0
- package/dist/services/tool-context-builder.d.ts +57 -0
- package/dist/services/tool-context-builder.d.ts.map +1 -0
- package/dist/services/tool-context-builder.js +125 -0
- package/dist/services/tool-context-builder.js.map +1 -0
- package/dist/services/tool-protection.service.d.ts +87 -10
- package/dist/services/tool-protection.service.d.ts.map +1 -1
- package/dist/services/tool-protection.service.js +282 -112
- package/dist/services/tool-protection.service.js.map +1 -1
- package/dist/types/oauth-required-error.d.ts +40 -0
- package/dist/types/oauth-required-error.d.ts.map +1 -0
- package/dist/types/oauth-required-error.js +40 -0
- package/dist/types/oauth-required-error.js.map +1 -0
- package/dist/utils/did-helpers.d.ts +33 -0
- package/dist/utils/did-helpers.d.ts.map +1 -1
- package/dist/utils/did-helpers.js +40 -0
- package/dist/utils/did-helpers.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/docs/API_REFERENCE.md +1362 -0
- package/docs/COMPLIANCE_MATRIX.md +691 -0
- package/docs/STATUSLIST2021_GUIDE.md +696 -0
- package/docs/W3C_VC_DELEGATION_GUIDE.md +710 -0
- package/package.json +24 -50
- package/scripts/audit-compliance.ts +724 -0
- package/src/__tests__/cache/tool-protection-cache.test.ts +640 -0
- package/src/__tests__/config/provider-runtime-config.test.ts +309 -0
- package/src/__tests__/delegation-e2e.test.ts +690 -0
- package/src/__tests__/identity/user-did-manager.test.ts +213 -0
- package/src/__tests__/index.test.ts +56 -0
- package/src/__tests__/integration/full-flow.test.ts +776 -0
- package/src/__tests__/integration.test.ts +281 -0
- package/src/__tests__/providers/base.test.ts +173 -0
- package/src/__tests__/providers/memory.test.ts +319 -0
- package/src/__tests__/regression/phase2-regression.test.ts +427 -0
- package/src/__tests__/runtime/audit-logger.test.ts +154 -0
- package/src/__tests__/runtime/base-extensions.test.ts +593 -0
- package/src/__tests__/runtime/base.test.ts +869 -0
- package/src/__tests__/runtime/delegation-flow.test.ts +164 -0
- package/src/__tests__/runtime/proof-client-did.test.ts +375 -0
- package/src/__tests__/runtime/route-interception.test.ts +686 -0
- package/src/__tests__/runtime/tool-protection-enforcement.test.ts +908 -0
- package/src/__tests__/services/agentshield-integration.test.ts +784 -0
- package/src/__tests__/services/provider-resolver-edge-cases.test.ts +487 -0
- package/src/__tests__/services/tool-protection-oauth-provider.test.ts +480 -0
- package/src/__tests__/services/tool-protection.service.test.ts +1366 -0
- package/src/__tests__/utils/mock-providers.ts +340 -0
- package/src/cache/oauth-config-cache.d.ts +69 -0
- package/src/cache/oauth-config-cache.d.ts.map +1 -0
- package/src/cache/oauth-config-cache.js +71 -0
- package/src/cache/oauth-config-cache.js.map +1 -0
- package/src/cache/oauth-config-cache.ts +123 -0
- package/src/cache/tool-protection-cache.ts +171 -0
- package/src/compliance/EXAMPLE.md +412 -0
- package/src/compliance/__tests__/schema-verifier.test.ts +797 -0
- package/src/compliance/index.ts +8 -0
- package/src/compliance/schema-registry.ts +460 -0
- package/src/compliance/schema-verifier.ts +708 -0
- package/src/config/__tests__/remote-config.spec.ts +268 -0
- package/src/config/remote-config.ts +174 -0
- package/src/config.ts +309 -0
- package/src/delegation/__tests__/audience-validator.test.ts +112 -0
- package/src/delegation/__tests__/bitstring.test.ts +346 -0
- package/src/delegation/__tests__/cascading-revocation.test.ts +628 -0
- package/src/delegation/__tests__/delegation-graph.test.ts +584 -0
- package/src/delegation/__tests__/utils.test.ts +152 -0
- package/src/delegation/__tests__/vc-issuer.test.ts +442 -0
- package/src/delegation/__tests__/vc-verifier.test.ts +922 -0
- package/src/delegation/audience-validator.ts +52 -0
- package/src/delegation/bitstring.ts +278 -0
- package/src/delegation/cascading-revocation.ts +370 -0
- package/src/delegation/delegation-graph.ts +299 -0
- package/src/delegation/index.ts +14 -0
- package/src/delegation/statuslist-manager.ts +353 -0
- package/src/delegation/storage/__tests__/memory-graph-storage.test.ts +366 -0
- package/src/delegation/storage/__tests__/memory-statuslist-storage.test.ts +228 -0
- package/src/delegation/storage/index.ts +9 -0
- package/src/delegation/storage/memory-graph-storage.ts +178 -0
- package/src/delegation/storage/memory-statuslist-storage.ts +77 -0
- package/src/delegation/utils.ts +42 -0
- package/src/delegation/vc-issuer.ts +232 -0
- package/src/delegation/vc-verifier.ts +568 -0
- package/src/identity/idp-token-resolver.ts +147 -0
- package/src/identity/idp-token-storage.interface.ts +59 -0
- package/src/identity/user-did-manager.ts +370 -0
- package/src/index.ts +260 -0
- package/src/providers/base.d.ts +91 -0
- package/src/providers/base.d.ts.map +1 -0
- package/src/providers/base.js +38 -0
- package/src/providers/base.js.map +1 -0
- package/src/providers/base.ts +96 -0
- package/src/providers/memory.ts +142 -0
- package/src/runtime/audit-logger.ts +39 -0
- package/src/runtime/base.ts +1329 -0
- package/src/services/__tests__/access-control.integration.test.ts +443 -0
- package/src/services/__tests__/access-control.proof-response-validation.test.ts +578 -0
- package/src/services/__tests__/access-control.service.test.ts +970 -0
- package/src/services/__tests__/batch-delegation.service.test.ts +351 -0
- package/src/services/__tests__/crypto.service.test.ts +531 -0
- package/src/services/__tests__/oauth-provider-registry.test.ts +142 -0
- package/src/services/__tests__/proof-verifier.integration.test.ts +485 -0
- package/src/services/__tests__/proof-verifier.test.ts +489 -0
- package/src/services/__tests__/provider-resolution.integration.test.ts +198 -0
- package/src/services/__tests__/provider-resolver.test.ts +217 -0
- package/src/services/__tests__/storage.service.test.ts +358 -0
- package/src/services/access-control.service.ts +990 -0
- package/src/services/authorization/authorization-registry.ts +66 -0
- package/src/services/authorization/types.ts +71 -0
- package/src/services/batch-delegation.service.ts +137 -0
- package/src/services/crypto.service.ts +302 -0
- package/src/services/errors.ts +76 -0
- package/src/services/index.ts +9 -0
- package/src/services/oauth-config.service.d.ts +53 -0
- package/src/services/oauth-config.service.d.ts.map +1 -0
- package/src/services/oauth-config.service.js +113 -0
- package/src/services/oauth-config.service.js.map +1 -0
- package/src/services/oauth-config.service.ts +166 -0
- package/src/services/oauth-provider-registry.d.ts +57 -0
- package/src/services/oauth-provider-registry.d.ts.map +1 -0
- package/src/services/oauth-provider-registry.js +73 -0
- package/src/services/oauth-provider-registry.js.map +1 -0
- package/src/services/oauth-provider-registry.ts +123 -0
- package/src/services/oauth-service.ts +510 -0
- package/src/services/oauth-token-retrieval.service.ts +245 -0
- package/src/services/proof-verifier.ts +478 -0
- package/src/services/provider-resolver.d.ts +48 -0
- package/src/services/provider-resolver.d.ts.map +1 -0
- package/src/services/provider-resolver.js +106 -0
- package/src/services/provider-resolver.js.map +1 -0
- package/src/services/provider-resolver.ts +144 -0
- package/src/services/provider-validator.ts +170 -0
- package/src/services/storage.service.ts +566 -0
- package/src/services/tool-context-builder.ts +172 -0
- package/src/services/tool-protection.service.ts +958 -0
- package/src/types/oauth-required-error.ts +63 -0
- package/src/types/tool-protection.ts +155 -0
- package/src/utils/__tests__/did-helpers.test.ts +101 -0
- package/src/utils/base64.ts +148 -0
- package/src/utils/cors.ts +83 -0
- package/src/utils/did-helpers.ts +150 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/storage-keys.ts +278 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +56 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { canonicalizeJSON } from "../utils.js";
|
|
3
|
+
|
|
4
|
+
describe("canonicalizeJSON", () => {
|
|
5
|
+
it("should canonicalize null", () => {
|
|
6
|
+
expect(canonicalizeJSON(null)).toBe("null");
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("should canonicalize boolean true", () => {
|
|
10
|
+
expect(canonicalizeJSON(true)).toBe("true");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("should canonicalize boolean false", () => {
|
|
14
|
+
expect(canonicalizeJSON(false)).toBe("false");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("should canonicalize number zero", () => {
|
|
18
|
+
expect(canonicalizeJSON(0)).toBe("0");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should canonicalize positive number", () => {
|
|
22
|
+
expect(canonicalizeJSON(42)).toBe("42");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should canonicalize negative number", () => {
|
|
26
|
+
expect(canonicalizeJSON(-42)).toBe("-42");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should canonicalize decimal number", () => {
|
|
30
|
+
expect(canonicalizeJSON(3.14)).toBe("3.14");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should throw error for non-finite number (Infinity)", () => {
|
|
34
|
+
expect(() => canonicalizeJSON(Infinity)).toThrow("Cannot canonicalize non-finite number");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should throw error for non-finite number (NaN)", () => {
|
|
38
|
+
expect(() => canonicalizeJSON(NaN)).toThrow("Cannot canonicalize non-finite number");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should canonicalize empty string", () => {
|
|
42
|
+
expect(canonicalizeJSON("")).toBe('""');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should canonicalize string", () => {
|
|
46
|
+
expect(canonicalizeJSON("hello")).toBe('"hello"');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should canonicalize string with special characters", () => {
|
|
50
|
+
expect(canonicalizeJSON('hello "world"')).toBe('"hello \\"world\\""');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should canonicalize empty array", () => {
|
|
54
|
+
expect(canonicalizeJSON([])).toBe("[]");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should canonicalize array with primitives", () => {
|
|
58
|
+
const result = canonicalizeJSON([1, "two", true, null]);
|
|
59
|
+
expect(result).toBe('[1,"two",true,null]');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should canonicalize nested arrays", () => {
|
|
63
|
+
const result = canonicalizeJSON([[1, 2], [3, 4]]);
|
|
64
|
+
expect(result).toBe("[[1,2],[3,4]]");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should canonicalize empty object", () => {
|
|
68
|
+
expect(canonicalizeJSON({})).toBe("{}");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should canonicalize object with sorted keys", () => {
|
|
72
|
+
const obj = { z: 3, a: 1, m: 2 };
|
|
73
|
+
const result = canonicalizeJSON(obj);
|
|
74
|
+
expect(result).toBe('{"a":1,"m":2,"z":3}');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should canonicalize object with string values", () => {
|
|
78
|
+
const obj = { name: "test", value: "hello" };
|
|
79
|
+
const result = canonicalizeJSON(obj);
|
|
80
|
+
expect(result).toBe('{"name":"test","value":"hello"}');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should canonicalize nested objects", () => {
|
|
84
|
+
const obj = { a: { b: { c: 1 } } };
|
|
85
|
+
const result = canonicalizeJSON(obj);
|
|
86
|
+
expect(result).toBe('{"a":{"b":{"c":1}}}');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should canonicalize object with array values", () => {
|
|
90
|
+
const obj = { items: [1, 2, 3] };
|
|
91
|
+
const result = canonicalizeJSON(obj);
|
|
92
|
+
expect(result).toBe('{"items":[1,2,3]}');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should canonicalize complex nested structure", () => {
|
|
96
|
+
const obj = {
|
|
97
|
+
z: "last",
|
|
98
|
+
a: {
|
|
99
|
+
nested: [1, { deep: true }],
|
|
100
|
+
value: 42,
|
|
101
|
+
},
|
|
102
|
+
m: ["array", "values"],
|
|
103
|
+
};
|
|
104
|
+
const result = canonicalizeJSON(obj);
|
|
105
|
+
// Keys should be sorted: a, m, z
|
|
106
|
+
expect(result).toContain('"a"');
|
|
107
|
+
expect(result).toContain('"m"');
|
|
108
|
+
expect(result).toContain('"z"');
|
|
109
|
+
expect(result.indexOf('"a"')).toBeLessThan(result.indexOf('"m"'));
|
|
110
|
+
expect(result.indexOf('"m"')).toBeLessThan(result.indexOf('"z"'));
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should produce identical output for same input (deterministic)", () => {
|
|
114
|
+
const obj = { z: 3, a: 1, m: 2 };
|
|
115
|
+
const result1 = canonicalizeJSON(obj);
|
|
116
|
+
const result2 = canonicalizeJSON(obj);
|
|
117
|
+
expect(result1).toBe(result2);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should handle object with null values", () => {
|
|
121
|
+
const obj = { a: null, b: "value" };
|
|
122
|
+
const result = canonicalizeJSON(obj);
|
|
123
|
+
expect(result).toBe('{"a":null,"b":"value"}');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("should handle object with boolean values", () => {
|
|
127
|
+
const obj = { enabled: true, disabled: false };
|
|
128
|
+
const result = canonicalizeJSON(obj);
|
|
129
|
+
expect(result).toBe('{"disabled":false,"enabled":true}');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should throw error for undefined", () => {
|
|
133
|
+
expect(() => canonicalizeJSON(undefined)).toThrow("Cannot canonicalize type: undefined");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("should throw error for function", () => {
|
|
137
|
+
expect(() => canonicalizeJSON(() => {})).toThrow("Cannot canonicalize type: function");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should handle unicode strings", () => {
|
|
141
|
+
const result = canonicalizeJSON("hello 世界");
|
|
142
|
+
expect(result).toBe('"hello 世界"');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("should handle object with numeric keys (sorted as strings)", () => {
|
|
146
|
+
const obj = { "10": "ten", "2": "two", "1": "one" };
|
|
147
|
+
const result = canonicalizeJSON(obj);
|
|
148
|
+
// Keys sorted lexicographically: "1", "10", "2"
|
|
149
|
+
expect(result).toBe('{"1":"one","10":"ten","2":"two"}');
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi, Mock } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
DelegationCredentialIssuer,
|
|
4
|
+
createDelegationIssuer,
|
|
5
|
+
type VCSigningFunction,
|
|
6
|
+
type IdentityProvider,
|
|
7
|
+
type IssueDelegationOptions,
|
|
8
|
+
} from "../vc-issuer.js";
|
|
9
|
+
import type {
|
|
10
|
+
DelegationRecord,
|
|
11
|
+
DelegationCredential,
|
|
12
|
+
Proof,
|
|
13
|
+
} from "@kya-os/contracts";
|
|
14
|
+
|
|
15
|
+
// Mock the contracts functions
|
|
16
|
+
vi.mock("@kya-os/contracts", () => ({
|
|
17
|
+
wrapDelegationAsVC: vi.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
vi.mock("../utils", () => ({
|
|
21
|
+
canonicalizeJSON: vi.fn(),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
describe("DelegationCredentialIssuer", () => {
|
|
25
|
+
let mockIdentity: IdentityProvider;
|
|
26
|
+
let mockSigningFunction: Mock<VCSigningFunction>;
|
|
27
|
+
let issuer: DelegationCredentialIssuer;
|
|
28
|
+
|
|
29
|
+
const mockDelegationRecord: DelegationRecord = {
|
|
30
|
+
id: "del-001",
|
|
31
|
+
issuerDid: "did:web:example.com:issuer",
|
|
32
|
+
subjectDid: "did:web:example.com:subject",
|
|
33
|
+
vcId: "urn:uuid:del-001",
|
|
34
|
+
parentId: undefined,
|
|
35
|
+
constraints: {
|
|
36
|
+
scopes: ["read:profile"],
|
|
37
|
+
},
|
|
38
|
+
signature: "",
|
|
39
|
+
status: "active",
|
|
40
|
+
createdAt: Date.now(),
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const mockProof: Proof = {
|
|
44
|
+
type: "Ed25519Signature2020",
|
|
45
|
+
created: "2024-01-01T00:00:00Z",
|
|
46
|
+
verificationMethod: "did:web:example.com:issuer#key-1",
|
|
47
|
+
proofPurpose: "assertionMethod",
|
|
48
|
+
proofValue: "mock-proof-value",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
beforeEach(async () => {
|
|
52
|
+
vi.clearAllMocks();
|
|
53
|
+
|
|
54
|
+
mockIdentity = {
|
|
55
|
+
getDid: vi.fn().mockReturnValue("did:web:example.com:issuer"),
|
|
56
|
+
getKeyId: vi.fn().mockReturnValue("key-123"),
|
|
57
|
+
getPrivateKey: vi.fn().mockReturnValue("mock-private-key"),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
mockSigningFunction = vi.fn().mockResolvedValue(mockProof);
|
|
61
|
+
|
|
62
|
+
issuer = new DelegationCredentialIssuer(mockIdentity, mockSigningFunction);
|
|
63
|
+
|
|
64
|
+
// Setup default mocks
|
|
65
|
+
const { wrapDelegationAsVC } = await import("@kya-os/contracts");
|
|
66
|
+
const { canonicalizeJSON } = await import("../utils.js");
|
|
67
|
+
|
|
68
|
+
vi.mocked(wrapDelegationAsVC).mockReturnValue({
|
|
69
|
+
"@context": ["https://www.w3.org/2018/credentials/v1"],
|
|
70
|
+
id: "urn:uuid:del-001",
|
|
71
|
+
type: ["VerifiableCredential", "DelegationCredential"],
|
|
72
|
+
issuer: "did:web:example.com:issuer",
|
|
73
|
+
issuanceDate: "2024-01-01T00:00:00Z",
|
|
74
|
+
credentialSubject: {
|
|
75
|
+
id: "did:web:example.com:subject",
|
|
76
|
+
delegation: {
|
|
77
|
+
id: "del-001",
|
|
78
|
+
issuerDid: "did:web:example.com:issuer",
|
|
79
|
+
subjectDid: "did:web:example.com:subject",
|
|
80
|
+
scopes: ["read:profile"],
|
|
81
|
+
status: "active",
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
} as any);
|
|
85
|
+
|
|
86
|
+
vi.mocked(canonicalizeJSON).mockReturnValue('{"canonical":"json"}');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe("constructor", () => {
|
|
90
|
+
it("should create issuer with identity and signing function", () => {
|
|
91
|
+
const newIssuer = new DelegationCredentialIssuer(
|
|
92
|
+
mockIdentity,
|
|
93
|
+
mockSigningFunction
|
|
94
|
+
);
|
|
95
|
+
expect(newIssuer).toBeInstanceOf(DelegationCredentialIssuer);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe("createDelegationIssuer", () => {
|
|
100
|
+
it("should create an issuer instance", () => {
|
|
101
|
+
const newIssuer = createDelegationIssuer(
|
|
102
|
+
mockIdentity,
|
|
103
|
+
mockSigningFunction
|
|
104
|
+
);
|
|
105
|
+
expect(newIssuer).toBeInstanceOf(DelegationCredentialIssuer);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe("issueDelegationCredential", () => {
|
|
110
|
+
it("should issue a signed delegation credential", async () => {
|
|
111
|
+
const result =
|
|
112
|
+
await issuer.issueDelegationCredential(mockDelegationRecord);
|
|
113
|
+
|
|
114
|
+
expect(result).toBeDefined();
|
|
115
|
+
expect(result.proof).toEqual(mockProof);
|
|
116
|
+
expect(mockSigningFunction).toHaveBeenCalled();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should call wrapDelegationAsVC with delegation record", async () => {
|
|
120
|
+
const { wrapDelegationAsVC } = await import("@kya-os/contracts");
|
|
121
|
+
|
|
122
|
+
await issuer.issueDelegationCredential(mockDelegationRecord);
|
|
123
|
+
|
|
124
|
+
expect(wrapDelegationAsVC).toHaveBeenCalledWith(
|
|
125
|
+
mockDelegationRecord,
|
|
126
|
+
expect.objectContaining({
|
|
127
|
+
id: undefined,
|
|
128
|
+
issuanceDate: undefined,
|
|
129
|
+
expirationDate: undefined,
|
|
130
|
+
credentialStatus: undefined,
|
|
131
|
+
})
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("should pass options to wrapDelegationAsVC", async () => {
|
|
136
|
+
const { wrapDelegationAsVC } = await import("@kya-os/contracts");
|
|
137
|
+
const options: IssueDelegationOptions = {
|
|
138
|
+
id: "custom-vc-id",
|
|
139
|
+
issuanceDate: "2024-01-01T00:00:00Z",
|
|
140
|
+
expirationDate: "2025-01-01T00:00:00Z",
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
await issuer.issueDelegationCredential(mockDelegationRecord, options);
|
|
144
|
+
|
|
145
|
+
expect(wrapDelegationAsVC).toHaveBeenCalledWith(
|
|
146
|
+
mockDelegationRecord,
|
|
147
|
+
expect.objectContaining({
|
|
148
|
+
id: "custom-vc-id",
|
|
149
|
+
issuanceDate: "2024-01-01T00:00:00Z",
|
|
150
|
+
expirationDate: "2025-01-01T00:00:00Z",
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("should add additional contexts if provided", async () => {
|
|
156
|
+
const { wrapDelegationAsVC } = await import("@kya-os/contracts");
|
|
157
|
+
const { canonicalizeJSON } = await import("../utils.js");
|
|
158
|
+
|
|
159
|
+
const mockVC = {
|
|
160
|
+
"@context": ["https://www.w3.org/2018/credentials/v1"],
|
|
161
|
+
id: "urn:uuid:del-001",
|
|
162
|
+
type: ["VerifiableCredential"],
|
|
163
|
+
issuer: "did:web:example.com:issuer",
|
|
164
|
+
issuanceDate: "2024-01-01T00:00:00Z",
|
|
165
|
+
credentialSubject: {},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
vi.mocked(wrapDelegationAsVC).mockReturnValue(mockVC as any);
|
|
169
|
+
|
|
170
|
+
const options: IssueDelegationOptions = {
|
|
171
|
+
additionalContexts: ["https://example.com/context"],
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const result = await issuer.issueDelegationCredential(
|
|
175
|
+
mockDelegationRecord,
|
|
176
|
+
options
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
expect(result["@context"]).toContain("https://example.com/context");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("should canonicalize VC before signing", async () => {
|
|
183
|
+
const { canonicalizeJSON } = await import("../utils.js");
|
|
184
|
+
|
|
185
|
+
await issuer.issueDelegationCredential(mockDelegationRecord);
|
|
186
|
+
|
|
187
|
+
expect(canonicalizeJSON).toHaveBeenCalled();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("should call signing function with canonical VC, DID, and key ID", async () => {
|
|
191
|
+
const { canonicalizeJSON } = await import("../utils.js");
|
|
192
|
+
vi.mocked(canonicalizeJSON).mockReturnValue("canonical-json-string");
|
|
193
|
+
|
|
194
|
+
await issuer.issueDelegationCredential(mockDelegationRecord);
|
|
195
|
+
|
|
196
|
+
expect(mockSigningFunction).toHaveBeenCalledWith(
|
|
197
|
+
"canonical-json-string",
|
|
198
|
+
"did:web:example.com:issuer",
|
|
199
|
+
"key-123"
|
|
200
|
+
);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("should include proof in returned credential", async () => {
|
|
204
|
+
const result =
|
|
205
|
+
await issuer.issueDelegationCredential(mockDelegationRecord);
|
|
206
|
+
|
|
207
|
+
expect(result.proof).toBeDefined();
|
|
208
|
+
expect(result.proof).toEqual(mockProof);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("should handle credential status option", async () => {
|
|
212
|
+
const { wrapDelegationAsVC } = await import("@kya-os/contracts");
|
|
213
|
+
const credentialStatus = {
|
|
214
|
+
id: "https://example.com/status#123",
|
|
215
|
+
type: "StatusList2021Entry" as const,
|
|
216
|
+
statusPurpose: "revocation" as const,
|
|
217
|
+
statusListIndex: "123",
|
|
218
|
+
statusListCredential: "https://example.com/status",
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const options: IssueDelegationOptions = {
|
|
222
|
+
credentialStatus,
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
await issuer.issueDelegationCredential(mockDelegationRecord, options);
|
|
226
|
+
|
|
227
|
+
expect(wrapDelegationAsVC).toHaveBeenCalledWith(
|
|
228
|
+
mockDelegationRecord,
|
|
229
|
+
expect.objectContaining({
|
|
230
|
+
credentialStatus,
|
|
231
|
+
})
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe("createAndIssueDelegation", () => {
|
|
237
|
+
it("should create delegation record and issue as VC", async () => {
|
|
238
|
+
const result = await issuer.createAndIssueDelegation({
|
|
239
|
+
id: "del-new",
|
|
240
|
+
issuerDid: "did:web:example.com:issuer",
|
|
241
|
+
subjectDid: "did:web:example.com:subject",
|
|
242
|
+
constraints: {
|
|
243
|
+
scopes: ["read:profile"],
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
expect(result).toBeDefined();
|
|
248
|
+
expect(result.proof).toEqual(mockProof);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it("should use provided vcId or generate one", async () => {
|
|
252
|
+
const { wrapDelegationAsVC } = await import("@kya-os/contracts");
|
|
253
|
+
|
|
254
|
+
// Without custom ID
|
|
255
|
+
await issuer.createAndIssueDelegation({
|
|
256
|
+
id: "del-new",
|
|
257
|
+
issuerDid: "did:web:example.com:issuer",
|
|
258
|
+
subjectDid: "did:web:example.com:subject",
|
|
259
|
+
constraints: {
|
|
260
|
+
scopes: ["read:profile"],
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
expect(wrapDelegationAsVC).toHaveBeenCalledWith(
|
|
265
|
+
expect.objectContaining({
|
|
266
|
+
vcId: "urn:uuid:del-new",
|
|
267
|
+
}),
|
|
268
|
+
expect.any(Object)
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// With custom ID
|
|
272
|
+
vi.clearAllMocks();
|
|
273
|
+
await issuer.createAndIssueDelegation(
|
|
274
|
+
{
|
|
275
|
+
id: "del-new",
|
|
276
|
+
issuerDid: "did:web:example.com:issuer",
|
|
277
|
+
subjectDid: "did:web:example.com:subject",
|
|
278
|
+
constraints: {
|
|
279
|
+
scopes: ["read:profile"],
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
{ id: "custom-vc-id" }
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
expect(wrapDelegationAsVC).toHaveBeenCalledWith(
|
|
286
|
+
expect.objectContaining({
|
|
287
|
+
vcId: "custom-vc-id",
|
|
288
|
+
}),
|
|
289
|
+
expect.any(Object)
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("should set default status to active", async () => {
|
|
294
|
+
const { wrapDelegationAsVC } = await import("@kya-os/contracts");
|
|
295
|
+
|
|
296
|
+
await issuer.createAndIssueDelegation({
|
|
297
|
+
id: "del-new",
|
|
298
|
+
issuerDid: "did:web:example.com:issuer",
|
|
299
|
+
subjectDid: "did:web:example.com:subject",
|
|
300
|
+
constraints: {
|
|
301
|
+
scopes: ["read:profile"],
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
expect(wrapDelegationAsVC).toHaveBeenCalledWith(
|
|
306
|
+
expect.objectContaining({
|
|
307
|
+
status: "active",
|
|
308
|
+
}),
|
|
309
|
+
expect.any(Object)
|
|
310
|
+
);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it("should use provided status if given", async () => {
|
|
314
|
+
const { wrapDelegationAsVC } = await import("@kya-os/contracts");
|
|
315
|
+
|
|
316
|
+
await issuer.createAndIssueDelegation({
|
|
317
|
+
id: "del-new",
|
|
318
|
+
issuerDid: "did:web:example.com:issuer",
|
|
319
|
+
subjectDid: "did:web:example.com:subject",
|
|
320
|
+
constraints: {
|
|
321
|
+
scopes: ["read:profile"],
|
|
322
|
+
},
|
|
323
|
+
status: "active",
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
expect(wrapDelegationAsVC).toHaveBeenCalledWith(
|
|
327
|
+
expect.objectContaining({
|
|
328
|
+
status: "active",
|
|
329
|
+
}),
|
|
330
|
+
expect.any(Object)
|
|
331
|
+
);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it("should include parentId if provided", async () => {
|
|
335
|
+
const { wrapDelegationAsVC } = await import("@kya-os/contracts");
|
|
336
|
+
|
|
337
|
+
await issuer.createAndIssueDelegation({
|
|
338
|
+
id: "del-child",
|
|
339
|
+
issuerDid: "did:web:example.com:issuer",
|
|
340
|
+
subjectDid: "did:web:example.com:subject",
|
|
341
|
+
parentId: "del-parent",
|
|
342
|
+
constraints: {
|
|
343
|
+
scopes: ["read:profile"],
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
expect(wrapDelegationAsVC).toHaveBeenCalledWith(
|
|
348
|
+
expect.objectContaining({
|
|
349
|
+
parentId: "del-parent",
|
|
350
|
+
}),
|
|
351
|
+
expect.any(Object)
|
|
352
|
+
);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it("should include metadata if provided", async () => {
|
|
356
|
+
const { wrapDelegationAsVC } = await import("@kya-os/contracts");
|
|
357
|
+
const metadata = { custom: "value" };
|
|
358
|
+
|
|
359
|
+
await issuer.createAndIssueDelegation({
|
|
360
|
+
id: "del-new",
|
|
361
|
+
issuerDid: "did:web:example.com:issuer",
|
|
362
|
+
subjectDid: "did:web:example.com:subject",
|
|
363
|
+
constraints: {
|
|
364
|
+
scopes: ["read:profile"],
|
|
365
|
+
},
|
|
366
|
+
metadata,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
expect(wrapDelegationAsVC).toHaveBeenCalledWith(
|
|
370
|
+
expect.objectContaining({
|
|
371
|
+
metadata,
|
|
372
|
+
}),
|
|
373
|
+
expect.any(Object)
|
|
374
|
+
);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it("should set createdAt timestamp", async () => {
|
|
378
|
+
const { wrapDelegationAsVC } = await import("@kya-os/contracts");
|
|
379
|
+
const beforeTime = Date.now();
|
|
380
|
+
|
|
381
|
+
await issuer.createAndIssueDelegation({
|
|
382
|
+
id: "del-new",
|
|
383
|
+
issuerDid: "did:web:example.com:issuer",
|
|
384
|
+
subjectDid: "did:web:example.com:subject",
|
|
385
|
+
constraints: {
|
|
386
|
+
scopes: ["read:profile"],
|
|
387
|
+
},
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
const afterTime = Date.now();
|
|
391
|
+
|
|
392
|
+
expect(wrapDelegationAsVC).toHaveBeenCalledWith(
|
|
393
|
+
expect.objectContaining({
|
|
394
|
+
createdAt: expect.any(Number),
|
|
395
|
+
}),
|
|
396
|
+
expect.any(Object)
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
const callArgs = vi.mocked(wrapDelegationAsVC).mock
|
|
400
|
+
.calls[0][0] as DelegationRecord;
|
|
401
|
+
expect(callArgs.createdAt).toBeGreaterThanOrEqual(beforeTime);
|
|
402
|
+
expect(callArgs.createdAt).toBeLessThanOrEqual(afterTime);
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
describe("getIssuerDid", () => {
|
|
407
|
+
it("should return issuer DID from identity provider", () => {
|
|
408
|
+
const did = issuer.getIssuerDid();
|
|
409
|
+
expect(did).toBe("did:web:example.com:issuer");
|
|
410
|
+
expect(mockIdentity.getDid).toHaveBeenCalled();
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
describe("getIssuerKeyId", () => {
|
|
415
|
+
it("should return key ID from identity provider", () => {
|
|
416
|
+
const keyId = issuer.getIssuerKeyId();
|
|
417
|
+
expect(keyId).toBe("key-123");
|
|
418
|
+
expect(mockIdentity.getKeyId).toHaveBeenCalled();
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
describe("error handling", () => {
|
|
423
|
+
it("should propagate errors from signing function", async () => {
|
|
424
|
+
mockSigningFunction.mockRejectedValue(new Error("Signing failed"));
|
|
425
|
+
|
|
426
|
+
await expect(
|
|
427
|
+
issuer.issueDelegationCredential(mockDelegationRecord)
|
|
428
|
+
).rejects.toThrow("Signing failed");
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it("should propagate errors from wrapDelegationAsVC", async () => {
|
|
432
|
+
const { wrapDelegationAsVC } = await import("@kya-os/contracts");
|
|
433
|
+
vi.mocked(wrapDelegationAsVC).mockImplementation(() => {
|
|
434
|
+
throw new Error("VC wrapping failed");
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
await expect(
|
|
438
|
+
issuer.issueDelegationCredential(mockDelegationRecord)
|
|
439
|
+
).rejects.toThrow("VC wrapping failed");
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
});
|