@datacules/agent-identity 0.9.0 → 0.11.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.
Files changed (51) hide show
  1. package/README.md +128 -0
  2. package/dist/cjs/attestation.js +131 -29
  3. package/dist/cjs/attestation.js.map +1 -1
  4. package/dist/cjs/identity-providers.js +100 -0
  5. package/dist/cjs/identity-providers.js.map +1 -0
  6. package/dist/cjs/index.js +5 -0
  7. package/dist/cjs/index.js.map +1 -1
  8. package/dist/cjs/revocation-listener.js +78 -0
  9. package/dist/cjs/revocation-listener.js.map +1 -0
  10. package/dist/cjs/revocation.js +59 -0
  11. package/dist/cjs/revocation.js.map +1 -0
  12. package/dist/cjs/rotation.js +6 -1
  13. package/dist/cjs/rotation.js.map +1 -1
  14. package/dist/cjs/router.js +27 -5
  15. package/dist/cjs/router.js.map +1 -1
  16. package/dist/cjs/schemas.js +26 -2
  17. package/dist/cjs/schemas.js.map +1 -1
  18. package/dist/esm/attestation.js +129 -28
  19. package/dist/esm/attestation.js.map +1 -1
  20. package/dist/esm/identity-providers.js +97 -0
  21. package/dist/esm/identity-providers.js.map +1 -0
  22. package/dist/esm/index.js +5 -0
  23. package/dist/esm/index.js.map +1 -1
  24. package/dist/esm/revocation-listener.js +74 -0
  25. package/dist/esm/revocation-listener.js.map +1 -0
  26. package/dist/esm/revocation.js +55 -0
  27. package/dist/esm/revocation.js.map +1 -0
  28. package/dist/esm/rotation.js +6 -1
  29. package/dist/esm/rotation.js.map +1 -1
  30. package/dist/esm/router.js +27 -5
  31. package/dist/esm/router.js.map +1 -1
  32. package/dist/esm/schemas.js +25 -1
  33. package/dist/esm/schemas.js.map +1 -1
  34. package/dist/types/attestation.d.ts +34 -6
  35. package/dist/types/attestation.d.ts.map +1 -1
  36. package/dist/types/identity-providers.d.ts +53 -0
  37. package/dist/types/identity-providers.d.ts.map +1 -0
  38. package/dist/types/index.d.ts +3 -0
  39. package/dist/types/index.d.ts.map +1 -1
  40. package/dist/types/revocation-listener.d.ts +63 -0
  41. package/dist/types/revocation-listener.d.ts.map +1 -0
  42. package/dist/types/revocation.d.ts +52 -0
  43. package/dist/types/revocation.d.ts.map +1 -0
  44. package/dist/types/rotation.d.ts.map +1 -1
  45. package/dist/types/router.d.ts +14 -0
  46. package/dist/types/router.d.ts.map +1 -1
  47. package/dist/types/schemas.d.ts +89 -4
  48. package/dist/types/schemas.d.ts.map +1 -1
  49. package/dist/types/types.d.ts +82 -1
  50. package/dist/types/types.d.ts.map +1 -1
  51. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,128 @@
1
+ <p align="center">
2
+ <img src="../../assets/logo.svg" alt="Agent Identity — by Datacules LLC" width="360"/>
3
+ </p>
4
+
5
+ # `@datacules/agent-identity`
6
+
7
+ Core credential routing engine for the agent-identity framework. Provider-agnostic; works with OpenAI, Anthropic, Gemini, Mistral, and local models.
8
+
9
+ Published as the `@datacules/agent-identity` npm package from `packages/core/`.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @datacules/agent-identity
15
+ ```
16
+
17
+ ## What's in this package
18
+
19
+ | Export | Description |
20
+ |--------|-------------|
21
+ | `createRouter` | Build a `CredentialRouter` from an array of credentials + rules |
22
+ | `createRouterFromStore` | Build a router backed by any `CredentialStore` |
23
+ | `createRouterWithConfig` | Full-featured factory — accepts attestation signer, budget enforcer, approval gate |
24
+ | `MemoryCredentialStore` | In-memory store for dev + tests |
25
+ | `CredentialRouter` | Class with `resolve()`, `resolveAsync()`, `resolvePair()`, `resolvePairAsync()` |
26
+ | `@datacules/agent-identity/schemas` | Zod schemas for `AgentRequestContext`, `MigrationContext`, `Credential` |
27
+ | `@datacules/agent-identity/react` | `useAgentIdentity` React hook (server-side credential resolution) |
28
+
29
+ ## Quick start
30
+
31
+ ```typescript
32
+ import { createRouter } from '@datacules/agent-identity';
33
+ import type { AgentRequestContext, Credential, RoutingRule } from '@datacules/agent-identity';
34
+
35
+ const credentials: Credential[] = [
36
+ {
37
+ id: 'cred-openai-prod',
38
+ ref: 'vault:openai-prod-key',
39
+ kind: 'fixed',
40
+ provider: 'openai',
41
+ status: 'active',
42
+ },
43
+ ];
44
+
45
+ const rules: RoutingRule[] = [
46
+ {
47
+ id: 'rule-default',
48
+ credentialRef: 'vault:openai-prod-key',
49
+ credentialKind: 'fixed',
50
+ priority: 10,
51
+ },
52
+ ];
53
+
54
+ const router = createRouter(credentials, rules);
55
+
56
+ const ctx: AgentRequestContext = {
57
+ userId: 'user-abc',
58
+ resourceId: 'knowledge-base',
59
+ resourceKind:'personal',
60
+ provider: 'openai',
61
+ model: 'gpt-4o',
62
+ action: 'read',
63
+ traceId: crypto.randomUUID(),
64
+ requestedAt: new Date().toISOString(),
65
+ };
66
+
67
+ const resolved = router.resolve(ctx);
68
+ // resolved.ref → 'vault:openai-prod-key' — look this up in your vault, server-side
69
+ // resolved.resolvedFor → 'service' (or the userId for user-delegated)
70
+ ```
71
+
72
+ ## Routing rule fields
73
+
74
+ ```typescript
75
+ const rule: RoutingRule = {
76
+ id: 'rule-personal-docs',
77
+ credentialRef: 'user-oauth-slot', // ref to a Credential
78
+ credentialKind: 'user-delegated', // 'fixed' | 'user-delegated'
79
+ priority: 10, // higher = evaluated first
80
+ resourceKind: 'personal', // optional match
81
+ matchProvider: 'anthropic', // optional match
82
+ matchAction: ['read', 'write'], // optional match
83
+ matchUserId: 'user-abc', // optional match
84
+ matchSpiffeId: 'spiffe://acme.com/ns/prod/sa/agent', // optional
85
+ matchPhase: 'extract', // migration phase match
86
+ canaryRef: 'user-oauth-slot-v2', // optional canary credential
87
+ canaryWeight: 5, // 0–100% traffic to canary
88
+ readOnly: true, // enforce read scope
89
+ };
90
+ ```
91
+
92
+ ## Migration: `resolvePair`
93
+
94
+ ```typescript
95
+ import type { MigrationContext } from '@datacules/agent-identity';
96
+
97
+ const pair = router.resolvePair(ctx as MigrationContext);
98
+ // pair.source — read credential for sourceResourceId
99
+ // pair.target — write credential for targetResourceId
100
+ // pair.expiresAt — earliest expiry of both
101
+ ```
102
+
103
+ ## Zod schemas
104
+
105
+ ```typescript
106
+ import { AgentRequestContextSchema } from '@datacules/agent-identity/schemas';
107
+
108
+ const parsed = AgentRequestContextSchema.safeParse(body);
109
+ if (!parsed.success) return Response.json({ error: parsed.error.flatten() }, { status: 400 });
110
+ ```
111
+
112
+ ## React hook
113
+
114
+ ```typescript
115
+ import { useAgentIdentity } from '@datacules/agent-identity/react';
116
+
117
+ function Component({ userId }: { userId: string }) {
118
+ const { resolvedFor, loading, error, expiresAt } = useAgentIdentity({
119
+ userId, resourceId: 'kb', resourceKind: 'personal',
120
+ provider: 'anthropic', model: 'claude-sonnet-4-20250514',
121
+ action: 'read', traceId: crypto.randomUUID(),
122
+ requestedAt: new Date().toISOString(),
123
+ });
124
+ // ...
125
+ }
126
+ ```
127
+
128
+ See the [root README](../../README.md) for the full API reference and all integration options.
@@ -1,42 +1,74 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HmacAttestationSigner = void 0;
3
+ exports.AsymmetricAttestationSigner = exports.HmacAttestationSigner = void 0;
4
4
  exports.buildAttestation = buildAttestation;
5
5
  exports.verifyAttestation = verifyAttestation;
6
- // ─── HMAC Signer (built-in, zero deps) ──────────────────────────────────────
6
+ // ─── Shared base64url helpers (module-level; used by both signers) ──────────
7
+ /** Encode a UTF-8 string to base64url */
8
+ function base64urlEncode(input) {
9
+ if (typeof Buffer !== 'undefined') {
10
+ return Buffer.from(input).toString('base64url');
11
+ }
12
+ return btoa(input).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
13
+ }
14
+ /** Encode an ArrayBuffer to base64url */
15
+ function bufToBase64url(buf) {
16
+ if (typeof Buffer !== 'undefined') {
17
+ return Buffer.from(buf).toString('base64url');
18
+ }
19
+ const bytes = new Uint8Array(buf);
20
+ let binary = '';
21
+ for (let i = 0; i < bytes.byteLength; i++)
22
+ binary += String.fromCharCode(bytes[i]);
23
+ return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
24
+ }
25
+ /**
26
+ * Decode a base64url string to a Uint8Array<ArrayBuffer>.
27
+ *
28
+ * Returns Uint8Array<ArrayBuffer> (not ArrayBufferLike) so the result is
29
+ * directly usable as BufferSource in crypto.subtle.verify() without an
30
+ * extra cast — required since TypeScript 5.5 made Uint8Array generic.
31
+ */
32
+ function base64urlToBuffer(s) {
33
+ if (typeof Buffer !== 'undefined') {
34
+ // Buffer.from() returns Uint8Array<ArrayBufferLike>; copy into a fresh
35
+ // Uint8Array<ArrayBuffer> so crypto.subtle accepts it as BufferSource.
36
+ const nodeBuf = Buffer.from(s, 'base64url');
37
+ const out = new Uint8Array(nodeBuf.length);
38
+ out.set(nodeBuf);
39
+ return out;
40
+ }
41
+ const b64 = s.replace(/-/g, '+').replace(/_/g, '/');
42
+ const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4);
43
+ const binary = atob(padded);
44
+ const bytes = new Uint8Array(binary.length);
45
+ for (let i = 0; i < binary.length; i++)
46
+ bytes[i] = binary.charCodeAt(i);
47
+ return bytes;
48
+ }
49
+ /** Decode a base64url body segment to a UTF-8 string */
50
+ function base64urlDecodeString(s) {
51
+ if (typeof Buffer !== 'undefined') {
52
+ return Buffer.from(s, 'base64url').toString('utf8');
53
+ }
54
+ return atob(s.replace(/-/g, '+').replace(/_/g, '/'));
55
+ }
56
+ // ─── HMAC Signer (built-in, zero deps) ───────────────────────────────────
7
57
  class HmacAttestationSigner {
8
58
  constructor(options) {
9
59
  this.secret = options.secret;
10
60
  this.issuer = options.issuer ?? 'agent-identity';
11
61
  this.ttlSeconds = options.ttlSeconds ?? 300;
12
62
  }
13
- base64url(input) {
14
- // Works in both browser and Node 18+ (Buffer is global in Node)
15
- if (typeof Buffer !== 'undefined') {
16
- return Buffer.from(input).toString('base64url');
17
- }
18
- // Browser fallback via btoa
19
- return btoa(input).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
20
- }
21
- bufToBase64url(buf) {
22
- if (typeof Buffer !== 'undefined') {
23
- return Buffer.from(buf).toString('base64url');
24
- }
25
- const bytes = new Uint8Array(buf);
26
- let binary = '';
27
- for (let i = 0; i < bytes.byteLength; i++)
28
- binary += String.fromCharCode(bytes[i]);
29
- return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
30
- }
31
63
  async hmacSign(data) {
32
64
  const enc = new TextEncoder();
33
65
  const key = await crypto.subtle.importKey('raw', enc.encode(this.secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
34
66
  const sig = await crypto.subtle.sign('HMAC', key, enc.encode(data));
35
- return this.bufToBase64url(sig);
67
+ return bufToBase64url(sig);
36
68
  }
37
69
  async sign(payload) {
38
- const header = this.base64url(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
39
- const body = this.base64url(JSON.stringify(payload));
70
+ const header = base64urlEncode(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
71
+ const body = base64urlEncode(JSON.stringify(payload));
40
72
  const sig = await this.hmacSign(`${header}.${body}`);
41
73
  return `${header}.${body}.${sig}`;
42
74
  }
@@ -48,11 +80,7 @@ class HmacAttestationSigner {
48
80
  const expected = await this.hmacSign(`${header}.${body}`);
49
81
  if (expected !== sig)
50
82
  return null;
51
- // Decode body: Node uses Buffer, browsers use atob
52
- const decoded = typeof Buffer !== 'undefined'
53
- ? Buffer.from(body, 'base64url').toString('utf8')
54
- : atob(body.replace(/-/g, '+').replace(/_/g, '/'));
55
- return JSON.parse(decoded);
83
+ return JSON.parse(base64urlDecodeString(body));
56
84
  }
57
85
  catch {
58
86
  return null;
@@ -60,6 +88,80 @@ class HmacAttestationSigner {
60
88
  }
61
89
  }
62
90
  exports.HmacAttestationSigner = HmacAttestationSigner;
91
+ // ─── Asymmetric Signer (RS256 / ES256) ──────────────────────────────────
92
+ /**
93
+ * Asymmetric JWT signer/verifier using Web Crypto (RS256 or ES256).
94
+ * Uses only crypto.subtle — no external dependencies.
95
+ *
96
+ * For signing (e.g. minting your own attestations):
97
+ * const signer = await AsymmetricAttestationSigner.fromKeyPair(privateKey, publicKey, 'RS256');
98
+ *
99
+ * For verification only (e.g. verifying incoming ID-JAGs from JWKS):
100
+ * const verifier = await AsymmetricAttestationSigner.fromPublicJwk(publicJwk, 'RS256');
101
+ */
102
+ class AsymmetricAttestationSigner {
103
+ constructor(privateKey, publicKey, algorithm, ttlSeconds) {
104
+ this.privateKey = privateKey;
105
+ this.publicKey = publicKey;
106
+ this.algorithm = algorithm;
107
+ this.ttlSeconds = ttlSeconds;
108
+ }
109
+ // ─── Static factory methods ──────────────────────────────────────────────
110
+ /**
111
+ * Create a signing+verification instance from an already-imported key pair.
112
+ */
113
+ static async fromKeyPair(privateKey, publicKey, algorithm, options) {
114
+ return new AsymmetricAttestationSigner(privateKey, publicKey, algorithm, options?.ttlSeconds ?? 300);
115
+ }
116
+ /**
117
+ * Create a verification-only instance from a JSON Web Key.
118
+ * Calling sign() on this instance will throw.
119
+ */
120
+ static async fromPublicJwk(jwk, algorithm) {
121
+ const importAlgo = algorithm === 'RS256'
122
+ ? { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }
123
+ : { name: 'ECDSA', namedCurve: 'P-256' };
124
+ const publicKey = await crypto.subtle.importKey('jwk', jwk, importAlgo, true, ['verify']);
125
+ return new AsymmetricAttestationSigner(null, publicKey, algorithm, 300);
126
+ }
127
+ // ─── Sign / Verify ────────────────────────────────────────────────────────────
128
+ async sign(payload) {
129
+ if (!this.privateKey) {
130
+ throw new Error('AsymmetricAttestationSigner: no private key — verification-only instance');
131
+ }
132
+ const header = base64urlEncode(JSON.stringify({ alg: this.algorithm, typ: 'JWT' }));
133
+ const body = base64urlEncode(JSON.stringify(payload));
134
+ const signingInput = `${header}.${body}`;
135
+ const data = new TextEncoder().encode(signingInput);
136
+ const algo = this.algorithm === 'RS256'
137
+ ? 'RSASSA-PKCS1-v1_5'
138
+ : { name: 'ECDSA', hash: 'SHA-256' };
139
+ const sigBuf = await crypto.subtle.sign(algo, this.privateKey, data);
140
+ const sig = bufToBase64url(sigBuf);
141
+ return `${header}.${body}.${sig}`;
142
+ }
143
+ async verify(token) {
144
+ try {
145
+ const [header, body, sig] = token.split('.');
146
+ if (!header || !body || !sig)
147
+ return null;
148
+ const signingInput = `${header}.${body}`;
149
+ const data = new TextEncoder().encode(signingInput);
150
+ const sigBytes = base64urlToBuffer(sig);
151
+ const algo = this.algorithm === 'RS256'
152
+ ? 'RSASSA-PKCS1-v1_5'
153
+ : { name: 'ECDSA', hash: 'SHA-256' };
154
+ const valid = await crypto.subtle.verify(algo, this.publicKey, sigBytes, data);
155
+ if (!valid)
156
+ return null;
157
+ return JSON.parse(base64urlDecodeString(body));
158
+ }
159
+ catch {
160
+ return null;
161
+ }
162
+ }
163
+ }
164
+ exports.AsymmetricAttestationSigner = AsymmetricAttestationSigner;
63
165
  async function buildAttestation(ctx, resolved, options) {
64
166
  const now = Math.floor(Date.now() / 1000);
65
167
  const payload = {
@@ -76,7 +178,7 @@ async function buildAttestation(ctx, resolved, options) {
76
178
  };
77
179
  return options.signer.sign(payload);
78
180
  }
79
- // ─── Standalone verifyAttestation helper ────────────────────────────────────
181
+ // ─── Standalone verifyAttestation helper ──────────────────────────────────
80
182
  async function verifyAttestation(token, signer) {
81
183
  const raw = await signer.verify(token);
82
184
  if (!raw)
@@ -1 +1 @@
1
- {"version":3,"file":"attestation.js","sourceRoot":"","sources":["../../src/attestation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"attestation.js","sourceRoot":"","sources":["../../src/attestation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ /**
3
+ * ID-JAG verification utilities — validates the claims on a decoded ID-JAG
4
+ * payload against a TrustedProviderRegistry.
5
+ *
6
+ * Signature verification is left to the caller (requires a JWT library or
7
+ * Web Crypto with the provider's JWKS). This module validates claims only.
8
+ *
9
+ * @module identity-providers
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.validateIdJagClaims = validateIdJagClaims;
13
+ // ─── validateIdJagClaims ──────────────────────────────────────────────────────
14
+ /**
15
+ * Validate ID-JAG claims (NOT signature — that's the caller's responsibility).
16
+ *
17
+ * Steps:
18
+ * 1. Find provider by payload.iss — return issuer_not_trusted if absent.
19
+ * 2. If provider.enabled === false — return provider_disabled.
20
+ * 3. If token is expired (with clock skew tolerance) — return expired.
21
+ * 4. If audience does not include the expected audience — return audience_mismatch.
22
+ * 5. If neither email_verified nor phone_number_verified — return missing_verified_identity.
23
+ * 6. If provider.requiredAmr is set and none of its values appear in payload.amr
24
+ * — return amr_not_satisfied.
25
+ * 7. Return { valid: true, provider }.
26
+ *
27
+ * @param payload Decoded JWT payload (signature NOT verified here)
28
+ * @param audience Expected aud (this service's authorization server URL)
29
+ * @param registry Configured trusted providers
30
+ * @param nowMs Current time in ms (injectable for testing; defaults to Date.now())
31
+ * @param clockSkewMs Accepted clock skew in ms (default: 120_000 = 2 minutes)
32
+ */
33
+ function validateIdJagClaims(payload, audience, registry, nowMs, clockSkewMs) {
34
+ const now = nowMs ?? Date.now();
35
+ const skew = clockSkewMs ?? 120000;
36
+ // 1. Issuer lookup
37
+ const provider = registry.providers.find((p) => p.issuerUrl === payload.iss);
38
+ if (!provider) {
39
+ return {
40
+ valid: false,
41
+ error: 'issuer_not_trusted',
42
+ errorMessage: `Issuer '${payload.iss}' is not in the trusted provider registry`,
43
+ };
44
+ }
45
+ // 2. Provider enabled check (undefined → enabled)
46
+ if (provider.enabled === false) {
47
+ return {
48
+ valid: false,
49
+ provider,
50
+ error: 'provider_disabled',
51
+ errorMessage: `Provider '${provider.label}' is currently disabled`,
52
+ };
53
+ }
54
+ // 3. Expiry check (exp is in seconds; add clock skew tolerance)
55
+ if (payload.exp * 1000 < now - skew) {
56
+ return {
57
+ valid: false,
58
+ provider,
59
+ error: 'expired',
60
+ errorMessage: `Token expired at ${new Date(payload.exp * 1000).toISOString()}`,
61
+ };
62
+ }
63
+ // 4. Audience check
64
+ const audiences = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
65
+ if (!audiences.includes(audience)) {
66
+ return {
67
+ valid: false,
68
+ provider,
69
+ error: 'audience_mismatch',
70
+ errorMessage: `Expected audience '${audience}' not found in token aud claim`,
71
+ };
72
+ }
73
+ // 5. Verified identity check — must have at least one verified identity claim
74
+ const hasVerifiedEmail = payload.email_verified === true;
75
+ const hasVerifiedPhone = payload.phone_number_verified === true;
76
+ if (!hasVerifiedEmail && !hasVerifiedPhone) {
77
+ return {
78
+ valid: false,
79
+ provider,
80
+ error: 'missing_verified_identity',
81
+ errorMessage: 'Token must have either email_verified=true or phone_number_verified=true',
82
+ };
83
+ }
84
+ // 6. AMR check
85
+ if (provider.requiredAmr && provider.requiredAmr.length > 0) {
86
+ const tokenAmr = payload.amr ?? [];
87
+ const satisfied = provider.requiredAmr.some((required) => tokenAmr.includes(required));
88
+ if (!satisfied) {
89
+ return {
90
+ valid: false,
91
+ provider,
92
+ error: 'amr_not_satisfied',
93
+ errorMessage: `Required AMR values [${provider.requiredAmr.join(', ')}] not found in token amr: [${tokenAmr.join(', ')}]`,
94
+ };
95
+ }
96
+ }
97
+ // 7. All checks passed
98
+ return { valid: true, provider };
99
+ }
100
+ //# sourceMappingURL=identity-providers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-providers.js","sourceRoot":"","sources":["../../src/identity-providers.ts"],"names":[],"mappings":"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"}
package/dist/cjs/index.js CHANGED
@@ -30,6 +30,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
30
30
  };
31
31
  Object.defineProperty(exports, "__esModule", { value: true });
32
32
  // ─── Runtime modules (classes, functions, const) ─────────────────────────────
33
+ // Core router + built-in stores
33
34
  __exportStar(require("./router"), exports);
34
35
  __exportStar(require("./providers"), exports);
35
36
  __exportStar(require("./credentials"), exports);
@@ -39,4 +40,8 @@ __exportStar(require("./attestation"), exports);
39
40
  __exportStar(require("./approval"), exports);
40
41
  __exportStar(require("./budget"), exports);
41
42
  __exportStar(require("./federation"), exports);
43
+ // auth.md compatibility — identity providers, revocation, and claim lifecycle
44
+ __exportStar(require("./identity-providers"), exports);
45
+ __exportStar(require("./revocation"), exports);
46
+ __exportStar(require("./revocation-listener"), exports);
42
47
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;;;;;;;;;;;;;;AAOH,gFAAgF;AAChF,2CAAyB;AACzB,8CAA4B;AAC5B,gDAA8B;AAC9B,6CAA2B;AAC3B,6CAA2B;AAC3B,gDAA8B;AAC9B,6CAA2B;AAC3B,2CAAyB;AACzB,+CAA6B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;;;;;;;;;;;;;;AAOH,gFAAgF;AAChF,gCAAgC;AAChC,2CAAyB;AACzB,8CAA4B;AAC5B,gDAA8B;AAC9B,6CAA2B;AAC3B,6CAA2B;AAC3B,gDAA8B;AAC9B,6CAA2B;AAC3B,2CAAyB;AACzB,+CAA6B;AAE7B,8EAA8E;AAC9E,uDAAqC;AACrC,+CAA6B;AAC7B,wDAAsC"}
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ /**
3
+ * RevocationListener — framework-agnostic inbound revocation handler.
4
+ *
5
+ * The listener does NOT handle JWKS fetching or JWT signature verification —
6
+ * those depend on external libraries. Pass a LogoutJwtVerifier that handles
7
+ * verification for your environment, then wire handleRequest() into your router.
8
+ *
9
+ * Express example:
10
+ * app.post('/agent/auth/revoke', async (req, res) => {
11
+ * const result = await listener.handleRequest(req.body, req.headers);
12
+ * res.status(result.httpStatus).json(result.body);
13
+ * });
14
+ *
15
+ * Fastify example:
16
+ * fastify.post('/agent/auth/revoke', async (req, reply) => {
17
+ * const result = await listener.handleRequest(req.body as string, req.headers);
18
+ * reply.code(result.httpStatus).send(result.body);
19
+ * });
20
+ *
21
+ * @module revocation-listener
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.RevocationListener = void 0;
25
+ // ─── RevocationListener ──────────────────────────────────────────────────
26
+ class RevocationListener {
27
+ constructor(opts) {
28
+ this.opts = opts;
29
+ }
30
+ /**
31
+ * Handle a raw revocation request body and headers.
32
+ *
33
+ * Processing steps:
34
+ * 1. Validate Content-Type contains 'application/logout+jwt'.
35
+ * 2. Call verifier.verify(rawBody) — if null, reject with 400.
36
+ * 3. Call handler.process(payload) — if replay, return 200 with 0 revoked.
37
+ * 4. Return 200 with credentialsRevoked count.
38
+ *
39
+ * @param rawBody The request body string (the logout+jwt token itself,
40
+ * per Content-Type: application/logout+jwt)
41
+ * @param headers Request headers (for Content-Type validation)
42
+ */
43
+ async handleRequest(rawBody, headers) {
44
+ // 1. Content-Type validation
45
+ const contentType = headers['content-type'] ?? headers['Content-Type'] ?? '';
46
+ const ctString = Array.isArray(contentType) ? contentType[0] : contentType;
47
+ if (!ctString.includes('application/logout+jwt')) {
48
+ return {
49
+ httpStatus: 400,
50
+ body: { error: 'invalid_content_type' },
51
+ };
52
+ }
53
+ // 2. Signature verification (delegated to caller-supplied verifier)
54
+ const payload = await this.opts.verifier.verify(rawBody);
55
+ if (!payload) {
56
+ return {
57
+ httpStatus: 400,
58
+ body: { error: 'invalid_logout_token' },
59
+ };
60
+ }
61
+ // 3. Process revocation (includes replay detection)
62
+ const result = await this.opts.handler.process(payload);
63
+ // Replay: return 200 with 0 revoked (idempotent)
64
+ if (result.replay) {
65
+ return {
66
+ httpStatus: 200,
67
+ body: { status: 'ok', credentialsRevoked: 0 },
68
+ };
69
+ }
70
+ // 4. Success
71
+ return {
72
+ httpStatus: 200,
73
+ body: { status: 'ok', credentialsRevoked: result.credentialsRevoked },
74
+ };
75
+ }
76
+ }
77
+ exports.RevocationListener = RevocationListener;
78
+ //# sourceMappingURL=revocation-listener.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revocation-listener.js","sourceRoot":"","sources":["../../src/revocation-listener.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;AA0BH,4EAA4E;AAE5E,MAAa,kBAAkB;IAC7B,YAA6B,IAA+B;QAA/B,SAAI,GAAJ,IAAI,CAA2B;IAAG,CAAC;IAEhE;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,aAAa,CACjB,OAAe,EACf,OAAsD;QAEtD,6BAA6B;QAC7B,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,IAAI,OAAO,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC7E,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;QAC3E,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,CAAC;YACjD,OAAO;gBACL,UAAU,EAAE,GAAG;gBACf,IAAI,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;aACxC,CAAC;QACJ,CAAC;QAED,oEAAoE;QACpE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;gBACL,UAAU,EAAE,GAAG;gBACf,IAAI,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;aACxC,CAAC;QACJ,CAAC;QAED,oDAAoD;QACpD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAExD,iDAAiD;QACjD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,OAAO;gBACL,UAAU,EAAE,GAAG;gBACf,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,EAAE;aAC9C,CAAC;QACJ,CAAC;QAED,aAAa;QACb,OAAO;YACL,UAAU,EAAE,GAAG;YACf,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,EAAE;SACtE,CAAC;IACJ,CAAC;CACF;AAxDD,gDAwDC"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ /**
3
+ * Inbound revocation handler — receives logout+jwt tokens from identity
4
+ * providers and propagates revocation to the CredentialStore.
5
+ *
6
+ * This module validates the logout+jwt STRUCTURE (does NOT verify the
7
+ * signature). The caller (e.g. an Express/Fastify route handler) is
8
+ * responsible for JWKS-based signature verification before passing the
9
+ * decoded payload here.
10
+ *
11
+ * @module revocation
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.RevocationHandler = void 0;
15
+ // ─── RevocationHandler ─────────────────────────────────────────────────────
16
+ /**
17
+ * RevocationHandler validates and processes inbound logout tokens.
18
+ *
19
+ * Usage:
20
+ * const handler = new RevocationHandler(store);
21
+ * // In your route: const payload = await verifyLogoutJwt(token, jwks); // caller's job
22
+ * const result = await handler.process(payload);
23
+ *
24
+ * The handler keeps an in-memory jti replay cache with configurable TTL.
25
+ * Stale entries are evicted lazily on each process() call.
26
+ */
27
+ class RevocationHandler {
28
+ constructor(store, options) {
29
+ this.store = store;
30
+ /**
31
+ * jti → processed-at timestamp (ms).
32
+ * Evict entries older than maxAgeMs.
33
+ */
34
+ this.seen = new Map();
35
+ this.maxAgeMs = options?.maxAgeMs ?? 10 * 60 * 1000; // 10 minutes default
36
+ }
37
+ async process(payload) {
38
+ this.evictStale();
39
+ // Replay detection
40
+ if (this.seen.has(payload.jti)) {
41
+ return { jti: payload.jti, credentialsRevoked: 0, replay: true };
42
+ }
43
+ this.seen.set(payload.jti, Date.now());
44
+ // Propagate revocation to the store (optional method; graceful if absent)
45
+ const count = this.store.revokeByIdentity
46
+ ? await this.store.revokeByIdentity(payload.iss, payload.sub, payload.aud)
47
+ : 0;
48
+ return { jti: payload.jti, credentialsRevoked: count, replay: false };
49
+ }
50
+ evictStale() {
51
+ const cutoff = Date.now() - this.maxAgeMs;
52
+ for (const [jti, ts] of this.seen) {
53
+ if (ts < cutoff)
54
+ this.seen.delete(jti);
55
+ }
56
+ }
57
+ }
58
+ exports.RevocationHandler = RevocationHandler;
59
+ //# sourceMappingURL=revocation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revocation.js","sourceRoot":"","sources":["../../src/revocation.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAsBH,8EAA8E;AAE9E;;;;;;;;;;GAUG;AACH,MAAa,iBAAiB;IAQ5B,YACmB,KAAsB,EACvC,OAA+B;QADd,UAAK,GAAL,KAAK,CAAiB;QARzC;;;WAGG;QACc,SAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;QAOhD,IAAI,CAAC,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,qBAAqB;IAC5E,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAA2B;QACvC,IAAI,CAAC,UAAU,EAAE,CAAC;QAElB,mBAAmB;QACnB,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/B,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,kBAAkB,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACnE,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAEvC,0EAA0E;QAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB;YACvC,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC;YAC1E,CAAC,CAAC,CAAC,CAAC;QAEN,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACxE,CAAC;IAEO,UAAU;QAChB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAClC,IAAI,EAAE,GAAG,MAAM;gBAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;CACF;AAtCD,8CAsCC"}
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CredentialRotationScheduler = void 0;
4
- // ─── CredentialRotationScheduler ─────────────────────────────────────────────
4
+ // ─── CredentialRotationScheduler ───────────────────────────────────────────────
5
5
  class CredentialRotationScheduler {
6
6
  constructor(repository, auditLogger) {
7
7
  this.repository = repository;
@@ -20,8 +20,13 @@ class CredentialRotationScheduler {
20
20
  const credentials = await this.repository.listActive();
21
21
  const now = new Date();
22
22
  for (const cred of credentials) {
23
+ // Skip credentials with no rotation policy
23
24
  if (!cred.rotation)
24
25
  continue;
26
+ // Skip unclaimed auth.md credentials — they cannot be rotated until
27
+ // the claim ceremony is complete and status flips to 'active'
28
+ if (cred.status === 'unclaimed')
29
+ continue;
25
30
  const due = this.isRotationDue(cred, cred.rotation, now);
26
31
  if (!due) {
27
32
  await this.maybeEmitWarning(cred, cred.rotation, now);