life-as-api 0.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.
Files changed (303) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +377 -0
  3. package/dist/agent/finance-agent.d.ts +44 -0
  4. package/dist/agent/finance-agent.js +75 -0
  5. package/dist/agent/finance-agent.js.map +1 -0
  6. package/dist/agent/legal-agent.d.ts +44 -0
  7. package/dist/agent/legal-agent.js +76 -0
  8. package/dist/agent/legal-agent.js.map +1 -0
  9. package/dist/agent/medical-agent.d.ts +44 -0
  10. package/dist/agent/medical-agent.js +75 -0
  11. package/dist/agent/medical-agent.js.map +1 -0
  12. package/dist/agent/trusted-agent-registry.d.ts +53 -0
  13. package/dist/agent/trusted-agent-registry.js +57 -0
  14. package/dist/agent/trusted-agent-registry.js.map +1 -0
  15. package/dist/audit/audit-chain-verifier.d.ts +10 -0
  16. package/dist/audit/audit-chain-verifier.js +67 -0
  17. package/dist/audit/audit-chain-verifier.js.map +1 -0
  18. package/dist/audit/audit-log.d.ts +48 -0
  19. package/dist/audit/audit-log.js +74 -0
  20. package/dist/audit/audit-log.js.map +1 -0
  21. package/dist/audit/sqlite-audit-log.d.ts +29 -0
  22. package/dist/audit/sqlite-audit-log.js +142 -0
  23. package/dist/audit/sqlite-audit-log.js.map +1 -0
  24. package/dist/auth/access-context-issuer.d.ts +18 -0
  25. package/dist/auth/access-context-issuer.js +88 -0
  26. package/dist/auth/access-context-issuer.js.map +1 -0
  27. package/dist/auth/assert-authorized-for-subject.d.ts +2 -0
  28. package/dist/auth/assert-authorized-for-subject.js +20 -0
  29. package/dist/auth/assert-authorized-for-subject.js.map +1 -0
  30. package/dist/auth/backend-context-minting-boundary.d.ts +23 -0
  31. package/dist/auth/backend-context-minting-boundary.js +281 -0
  32. package/dist/auth/backend-context-minting-boundary.js.map +1 -0
  33. package/dist/auth/default-authorization-policy.d.ts +7 -0
  34. package/dist/auth/default-authorization-policy.js +64 -0
  35. package/dist/auth/default-authorization-policy.js.map +1 -0
  36. package/dist/auth/fake-auth-adapter.d.ts +7 -0
  37. package/dist/auth/fake-auth-adapter.js +27 -0
  38. package/dist/auth/fake-auth-adapter.js.map +1 -0
  39. package/dist/auth/http-client.d.ts +12 -0
  40. package/dist/auth/http-client.js +7 -0
  41. package/dist/auth/http-client.js.map +1 -0
  42. package/dist/auth/in-memory-grant-resolver.d.ts +21 -0
  43. package/dist/auth/in-memory-grant-resolver.js +73 -0
  44. package/dist/auth/in-memory-grant-resolver.js.map +1 -0
  45. package/dist/auth/in-memory-tenant-boundary.d.ts +16 -0
  46. package/dist/auth/in-memory-tenant-boundary.js +37 -0
  47. package/dist/auth/in-memory-tenant-boundary.js.map +1 -0
  48. package/dist/auth/jwks-key-ring-auth-adapter.d.ts +38 -0
  49. package/dist/auth/jwks-key-ring-auth-adapter.js +123 -0
  50. package/dist/auth/jwks-key-ring-auth-adapter.js.map +1 -0
  51. package/dist/auth/jwks-key-ring.d.ts +25 -0
  52. package/dist/auth/jwks-key-ring.js +111 -0
  53. package/dist/auth/jwks-key-ring.js.map +1 -0
  54. package/dist/auth/jwt-auth-adapter.d.ts +15 -0
  55. package/dist/auth/jwt-auth-adapter.js +59 -0
  56. package/dist/auth/jwt-auth-adapter.js.map +1 -0
  57. package/dist/auth/jwt-core.d.ts +32 -0
  58. package/dist/auth/jwt-core.js +226 -0
  59. package/dist/auth/jwt-core.js.map +1 -0
  60. package/dist/auth/jwt-key-ring-auth-adapter.d.ts +16 -0
  61. package/dist/auth/jwt-key-ring-auth-adapter.js +58 -0
  62. package/dist/auth/jwt-key-ring-auth-adapter.js.map +1 -0
  63. package/dist/auth/oidc-discovery.d.ts +15 -0
  64. package/dist/auth/oidc-discovery.js +46 -0
  65. package/dist/auth/oidc-discovery.js.map +1 -0
  66. package/dist/auth/production-scoped-life-api.d.ts +58 -0
  67. package/dist/auth/production-scoped-life-api.js +802 -0
  68. package/dist/auth/production-scoped-life-api.js.map +1 -0
  69. package/dist/auth/proposal-scope-inspector.d.ts +3 -0
  70. package/dist/auth/proposal-scope-inspector.js +42 -0
  71. package/dist/auth/proposal-scope-inspector.js.map +1 -0
  72. package/dist/auth/proposal-scoped-life-api.d.ts +21 -0
  73. package/dist/auth/proposal-scoped-life-api.js +168 -0
  74. package/dist/auth/proposal-scoped-life-api.js.map +1 -0
  75. package/dist/auth/rate-limited-context-minting-boundary.d.ts +27 -0
  76. package/dist/auth/rate-limited-context-minting-boundary.js +77 -0
  77. package/dist/auth/rate-limited-context-minting-boundary.js.map +1 -0
  78. package/dist/auth/revocation-aware-auth-adapter.d.ts +21 -0
  79. package/dist/auth/revocation-aware-auth-adapter.js +88 -0
  80. package/dist/auth/revocation-aware-auth-adapter.js.map +1 -0
  81. package/dist/auth/sqlite-grant-resolver.d.ts +8 -0
  82. package/dist/auth/sqlite-grant-resolver.js +78 -0
  83. package/dist/auth/sqlite-grant-resolver.js.map +1 -0
  84. package/dist/auth/sqlite-tenant-boundary.d.ts +7 -0
  85. package/dist/auth/sqlite-tenant-boundary.js +41 -0
  86. package/dist/auth/sqlite-tenant-boundary.js.map +1 -0
  87. package/dist/auth/static-jwt-key-ring.d.ts +13 -0
  88. package/dist/auth/static-jwt-key-ring.js +73 -0
  89. package/dist/auth/static-jwt-key-ring.js.map +1 -0
  90. package/dist/auth/subject-scoped-life-api.d.ts +14 -0
  91. package/dist/auth/subject-scoped-life-api.js +51 -0
  92. package/dist/auth/subject-scoped-life-api.js.map +1 -0
  93. package/dist/auth/test-context-minting-boundary.d.ts +13 -0
  94. package/dist/auth/test-context-minting-boundary.js +74 -0
  95. package/dist/auth/test-context-minting-boundary.js.map +1 -0
  96. package/dist/auth/types.d.ts +77 -0
  97. package/dist/auth/types.js +16 -0
  98. package/dist/auth/types.js.map +1 -0
  99. package/dist/compliance/subject-data-report.d.ts +43 -0
  100. package/dist/compliance/subject-data-report.js +155 -0
  101. package/dist/compliance/subject-data-report.js.map +1 -0
  102. package/dist/consent/consent-guard.d.ts +4 -0
  103. package/dist/consent/consent-guard.js +37 -0
  104. package/dist/consent/consent-guard.js.map +1 -0
  105. package/dist/consent/grant-registry.d.ts +7 -0
  106. package/dist/consent/grant-registry.js +13 -0
  107. package/dist/consent/grant-registry.js.map +1 -0
  108. package/dist/consent/intersect-selector.d.ts +3 -0
  109. package/dist/consent/intersect-selector.js +66 -0
  110. package/dist/consent/intersect-selector.js.map +1 -0
  111. package/dist/contract/diagnostics.d.ts +32 -0
  112. package/dist/contract/diagnostics.js +112 -0
  113. package/dist/contract/diagnostics.js.map +1 -0
  114. package/dist/contract/project-contracts.d.ts +5 -0
  115. package/dist/contract/project-contracts.js +159 -0
  116. package/dist/contract/project-contracts.js.map +1 -0
  117. package/dist/contract/types.d.ts +32 -0
  118. package/dist/contract/types.js +5 -0
  119. package/dist/contract/types.js.map +1 -0
  120. package/dist/crypto/aes-gcm.d.ts +11 -0
  121. package/dist/crypto/aes-gcm.js +78 -0
  122. package/dist/crypto/aes-gcm.js.map +1 -0
  123. package/dist/crypto/claim-serializer.d.ts +26 -0
  124. package/dist/crypto/claim-serializer.js +59 -0
  125. package/dist/crypto/claim-serializer.js.map +1 -0
  126. package/dist/crypto/sha256.d.ts +12 -0
  127. package/dist/crypto/sha256.js +40 -0
  128. package/dist/crypto/sha256.js.map +1 -0
  129. package/dist/crypto/static-key.d.ts +11 -0
  130. package/dist/crypto/static-key.js +60 -0
  131. package/dist/crypto/static-key.js.map +1 -0
  132. package/dist/crypto/types.d.ts +20 -0
  133. package/dist/crypto/types.js +8 -0
  134. package/dist/crypto/types.js.map +1 -0
  135. package/dist/datasource/trusted-data-source-registry.d.ts +51 -0
  136. package/dist/datasource/trusted-data-source-registry.js +57 -0
  137. package/dist/datasource/trusted-data-source-registry.js.map +1 -0
  138. package/dist/erasure/tombstone.d.ts +13 -0
  139. package/dist/erasure/tombstone.js +20 -0
  140. package/dist/erasure/tombstone.js.map +1 -0
  141. package/dist/evidence/evidence-backed-claim-creator.d.ts +48 -0
  142. package/dist/evidence/evidence-backed-claim-creator.js +107 -0
  143. package/dist/evidence/evidence-backed-claim-creator.js.map +1 -0
  144. package/dist/evidence/in-memory-raw-document-store.d.ts +26 -0
  145. package/dist/evidence/in-memory-raw-document-store.js +84 -0
  146. package/dist/evidence/in-memory-raw-document-store.js.map +1 -0
  147. package/dist/evidence/raw-document-store.d.ts +12 -0
  148. package/dist/evidence/raw-document-store.js +8 -0
  149. package/dist/evidence/raw-document-store.js.map +1 -0
  150. package/dist/evidence/source-evidence-validator.d.ts +4 -0
  151. package/dist/evidence/source-evidence-validator.js +14 -0
  152. package/dist/evidence/source-evidence-validator.js.map +1 -0
  153. package/dist/evidence/sqlite-raw-document-store.d.ts +19 -0
  154. package/dist/evidence/sqlite-raw-document-store.js +122 -0
  155. package/dist/evidence/sqlite-raw-document-store.js.map +1 -0
  156. package/dist/guardian/guardian-registry.d.ts +96 -0
  157. package/dist/guardian/guardian-registry.js +95 -0
  158. package/dist/guardian/guardian-registry.js.map +1 -0
  159. package/dist/index.d.ts +44 -0
  160. package/dist/index.js +72 -0
  161. package/dist/index.js.map +1 -0
  162. package/dist/observability/health.d.ts +12 -0
  163. package/dist/observability/health.js +16 -0
  164. package/dist/observability/health.js.map +1 -0
  165. package/dist/observability/logger.d.ts +21 -0
  166. package/dist/observability/logger.js +40 -0
  167. package/dist/observability/logger.js.map +1 -0
  168. package/dist/observability/metrics.d.ts +15 -0
  169. package/dist/observability/metrics.js +52 -0
  170. package/dist/observability/metrics.js.map +1 -0
  171. package/dist/persistence/backup.d.ts +24 -0
  172. package/dist/persistence/backup.js +251 -0
  173. package/dist/persistence/backup.js.map +1 -0
  174. package/dist/persistence/decryptability-scan.d.ts +10 -0
  175. package/dist/persistence/decryptability-scan.js +144 -0
  176. package/dist/persistence/decryptability-scan.js.map +1 -0
  177. package/dist/persistence/in-memory-unit-of-work.d.ts +11 -0
  178. package/dist/persistence/in-memory-unit-of-work.js +32 -0
  179. package/dist/persistence/in-memory-unit-of-work.js.map +1 -0
  180. package/dist/persistence/key-availability.d.ts +9 -0
  181. package/dist/persistence/key-availability.js +125 -0
  182. package/dist/persistence/key-availability.js.map +1 -0
  183. package/dist/persistence/sqlite/migrations.d.ts +9 -0
  184. package/dist/persistence/sqlite/migrations.js +535 -0
  185. package/dist/persistence/sqlite/migrations.js.map +1 -0
  186. package/dist/persistence/sqlite/schema.d.ts +10 -0
  187. package/dist/persistence/sqlite/schema.js +101 -0
  188. package/dist/persistence/sqlite/schema.js.map +1 -0
  189. package/dist/persistence/sqlite/sqlite-fact-store.d.ts +24 -0
  190. package/dist/persistence/sqlite/sqlite-fact-store.js +207 -0
  191. package/dist/persistence/sqlite/sqlite-fact-store.js.map +1 -0
  192. package/dist/persistence/sqlite/sqlite-source-registry.d.ts +17 -0
  193. package/dist/persistence/sqlite/sqlite-source-registry.js +132 -0
  194. package/dist/persistence/sqlite/sqlite-source-registry.js.map +1 -0
  195. package/dist/persistence/sqlite/sqlite-unit-of-work.d.ts +8 -0
  196. package/dist/persistence/sqlite/sqlite-unit-of-work.js +41 -0
  197. package/dist/persistence/sqlite/sqlite-unit-of-work.js.map +1 -0
  198. package/dist/persistence/sqlite/types.d.ts +24 -0
  199. package/dist/persistence/sqlite/types.js +28 -0
  200. package/dist/persistence/sqlite/types.js.map +1 -0
  201. package/dist/persistence/unit-of-work.d.ts +6 -0
  202. package/dist/persistence/unit-of-work.js +15 -0
  203. package/dist/persistence/unit-of-work.js.map +1 -0
  204. package/dist/proposal/accepted-claim-appender.d.ts +7 -0
  205. package/dist/proposal/accepted-claim-appender.js +16 -0
  206. package/dist/proposal/accepted-claim-appender.js.map +1 -0
  207. package/dist/proposal/in-memory-proposal-queue.d.ts +21 -0
  208. package/dist/proposal/in-memory-proposal-queue.js +218 -0
  209. package/dist/proposal/in-memory-proposal-queue.js.map +1 -0
  210. package/dist/proposal/policy-aware-proposal-queue.d.ts +23 -0
  211. package/dist/proposal/policy-aware-proposal-queue.js +162 -0
  212. package/dist/proposal/policy-aware-proposal-queue.js.map +1 -0
  213. package/dist/proposal/proposal-policy.d.ts +37 -0
  214. package/dist/proposal/proposal-policy.js +72 -0
  215. package/dist/proposal/proposal-policy.js.map +1 -0
  216. package/dist/proposal/proposal-queue.d.ts +13 -0
  217. package/dist/proposal/proposal-queue.js +15 -0
  218. package/dist/proposal/proposal-queue.js.map +1 -0
  219. package/dist/proposal/source-aware-claim-appender.d.ts +13 -0
  220. package/dist/proposal/source-aware-claim-appender.js +47 -0
  221. package/dist/proposal/source-aware-claim-appender.js.map +1 -0
  222. package/dist/proposal/sqlite-proposal-queue.d.ts +26 -0
  223. package/dist/proposal/sqlite-proposal-queue.js +282 -0
  224. package/dist/proposal/sqlite-proposal-queue.js.map +1 -0
  225. package/dist/resolution/resolve-claims.d.ts +4 -0
  226. package/dist/resolution/resolve-claims.js +101 -0
  227. package/dist/resolution/resolve-claims.js.map +1 -0
  228. package/dist/resolution/trust-profile.d.ts +12 -0
  229. package/dist/resolution/trust-profile.js +23 -0
  230. package/dist/resolution/trust-profile.js.map +1 -0
  231. package/dist/resolution/types.d.ts +15 -0
  232. package/dist/resolution/types.js +8 -0
  233. package/dist/resolution/types.js.map +1 -0
  234. package/dist/sdk/create-backend-integrated-life-api-for-test.d.ts +23 -0
  235. package/dist/sdk/create-backend-integrated-life-api-for-test.js +240 -0
  236. package/dist/sdk/create-backend-integrated-life-api-for-test.js.map +1 -0
  237. package/dist/sdk/create-life-api.d.ts +55 -0
  238. package/dist/sdk/create-life-api.js +251 -0
  239. package/dist/sdk/create-life-api.js.map +1 -0
  240. package/dist/sdk/create-sqlite-life-api.d.ts +33 -0
  241. package/dist/sdk/create-sqlite-life-api.js +654 -0
  242. package/dist/sdk/create-sqlite-life-api.js.map +1 -0
  243. package/dist/sdk/life-api.d.ts +27 -0
  244. package/dist/sdk/life-api.js +17 -0
  245. package/dist/sdk/life-api.js.map +1 -0
  246. package/dist/sdk/validation.d.ts +8 -0
  247. package/dist/sdk/validation.js +216 -0
  248. package/dist/sdk/validation.js.map +1 -0
  249. package/dist/source/in-memory-source-registry.d.ts +15 -0
  250. package/dist/source/in-memory-source-registry.js +104 -0
  251. package/dist/source/in-memory-source-registry.js.map +1 -0
  252. package/dist/source/source-aware-fact-store.d.ts +19 -0
  253. package/dist/source/source-aware-fact-store.js +40 -0
  254. package/dist/source/source-aware-fact-store.js.map +1 -0
  255. package/dist/source/source-kind-predicate-policy.d.ts +4 -0
  256. package/dist/source/source-kind-predicate-policy.js +62 -0
  257. package/dist/source/source-kind-predicate-policy.js.map +1 -0
  258. package/dist/source/source-registry.d.ts +11 -0
  259. package/dist/source/source-registry.js +12 -0
  260. package/dist/source/source-registry.js.map +1 -0
  261. package/dist/store/in-memory-fact-store.d.ts +25 -0
  262. package/dist/store/in-memory-fact-store.js +192 -0
  263. package/dist/store/in-memory-fact-store.js.map +1 -0
  264. package/dist/surface/context-query.d.ts +21 -0
  265. package/dist/surface/context-query.js +113 -0
  266. package/dist/surface/context-query.js.map +1 -0
  267. package/dist/surface/evidence-backed-context-query.d.ts +62 -0
  268. package/dist/surface/evidence-backed-context-query.js +240 -0
  269. package/dist/surface/evidence-backed-context-query.js.map +1 -0
  270. package/dist/types/access-context.d.ts +25 -0
  271. package/dist/types/access-context.js +6 -0
  272. package/dist/types/access-context.js.map +1 -0
  273. package/dist/types/claim.d.ts +48 -0
  274. package/dist/types/claim.js +5 -0
  275. package/dist/types/claim.js.map +1 -0
  276. package/dist/types/consent.d.ts +41 -0
  277. package/dist/types/consent.js +3 -0
  278. package/dist/types/consent.js.map +1 -0
  279. package/dist/types/crypto-boundary.d.ts +12 -0
  280. package/dist/types/crypto-boundary.js +15 -0
  281. package/dist/types/crypto-boundary.js.map +1 -0
  282. package/dist/types/evidence.d.ts +31 -0
  283. package/dist/types/evidence.js +8 -0
  284. package/dist/types/evidence.js.map +1 -0
  285. package/dist/types/fact-store.d.ts +24 -0
  286. package/dist/types/fact-store.js +12 -0
  287. package/dist/types/fact-store.js.map +1 -0
  288. package/dist/types/ids.d.ts +36 -0
  289. package/dist/types/ids.js +11 -0
  290. package/dist/types/ids.js.map +1 -0
  291. package/dist/types/proposal.d.ts +38 -0
  292. package/dist/types/proposal.js +7 -0
  293. package/dist/types/proposal.js.map +1 -0
  294. package/dist/types/source.d.ts +33 -0
  295. package/dist/types/source.js +6 -0
  296. package/dist/types/source.js.map +1 -0
  297. package/dist/types/trust.d.ts +24 -0
  298. package/dist/types/trust.js +5 -0
  299. package/dist/types/trust.js.map +1 -0
  300. package/dist/utilities/deep-freeze.d.ts +1 -0
  301. package/dist/utilities/deep-freeze.js +17 -0
  302. package/dist/utilities/deep-freeze.js.map +1 -0
  303. package/package.json +55 -0
@@ -0,0 +1,21 @@
1
+ import type { Clock, GrantDescriptor, GrantResolver } from "./types.js";
2
+ /** Grant record for InMemoryGrantResolver. */
3
+ export interface GrantRecord {
4
+ readonly grantId: string;
5
+ readonly agentId: string;
6
+ readonly tenantId: string;
7
+ readonly notBefore: string;
8
+ readonly notAfter: string;
9
+ readonly status: "active" | "revoked";
10
+ readonly personId?: string;
11
+ }
12
+ /** @remarks TEST/DEV ONLY — not for production use. */
13
+ export declare class InMemoryGrantResolver implements GrantResolver {
14
+ private readonly _grants;
15
+ private readonly _clock;
16
+ private readonly _failWith;
17
+ constructor(grants: ReadonlyArray<GrantRecord>, clock: Clock, opts?: {
18
+ readonly failWith?: Error;
19
+ });
20
+ resolveActiveGrant(agentId: string, grantId: string, tenantId: string): Promise<GrantDescriptor | undefined>;
21
+ }
@@ -0,0 +1,73 @@
1
+ // TEST/DEV ONLY — InMemoryGrantResolver (Production-02a).
2
+ // Do NOT export from src/index.ts. Do NOT use in production.
3
+ //
4
+ // AUTH_GRANT_ACTIVE_CHECKED_001: resolveActiveGrant fires before every AgentConsentContext mint.
5
+ // AUTH_GRANT_REVOKED_DENIED_001: revoked grants return undefined — never active.
6
+ // AUTH_GRANT_EXPIRED_DENIED_001: grants where clock.now() >= notAfter return undefined.
7
+ // AUTH_GRANT_TENANT_SCOPE_ENFORCED_001: grant tenantId must match requesting tenantId.
8
+ // AUTH_GRANT_AGENT_SCOPE_ENFORCED_001: grant agentId must match requesting agentId.
9
+ //
10
+ // All denial cases return undefined (no existence leak):
11
+ // missing, revoked, expired, not-yet-active, tenant mismatch, agent mismatch → undefined
12
+ //
13
+ // Storage-failure behavior: if constructed with failWith, resolveActiveGrant throws that error
14
+ // — storage errors must never be silently converted to undefined.
15
+ // A real backend storage failure is not the same as "no active grant."
16
+ import { asPersonId } from "../types/ids.js";
17
+ /** @remarks TEST/DEV ONLY — not for production use. */
18
+ export class InMemoryGrantResolver {
19
+ _grants;
20
+ _clock;
21
+ _failWith;
22
+ constructor(grants, clock, opts) {
23
+ const map = new Map();
24
+ for (const g of grants) {
25
+ map.set(g.grantId, g);
26
+ }
27
+ this._grants = map;
28
+ this._clock = clock;
29
+ this._failWith = opts?.failWith;
30
+ }
31
+ async resolveActiveGrant(agentId, grantId, tenantId) {
32
+ // Storage-failure simulation — must throw, not return undefined
33
+ if (this._failWith !== undefined) {
34
+ throw this._failWith;
35
+ }
36
+ const record = this._grants.get(grantId);
37
+ // Missing grant — return undefined (no existence leak)
38
+ if (record === undefined) {
39
+ return undefined;
40
+ }
41
+ // AUTH_GRANT_AGENT_SCOPE_ENFORCED_001: agentId must match
42
+ if (record.agentId !== agentId) {
43
+ return undefined;
44
+ }
45
+ // AUTH_GRANT_TENANT_SCOPE_ENFORCED_001: tenantId must match
46
+ if (record.tenantId !== tenantId) {
47
+ return undefined;
48
+ }
49
+ // AUTH_GRANT_REVOKED_DENIED_001: revoked grants are permanently denied
50
+ if (record.status === "revoked") {
51
+ return undefined;
52
+ }
53
+ const now = this._clock.now();
54
+ // Not-yet-active: before notBefore
55
+ if (now < record.notBefore) {
56
+ return undefined;
57
+ }
58
+ // AUTH_GRANT_EXPIRED_DENIED_001: expired at or after notAfter
59
+ if (now >= record.notAfter) {
60
+ return undefined;
61
+ }
62
+ // AUTH_GRANT_ACTIVE_CHECKED_001: all checks passed — return descriptor
63
+ return {
64
+ grantId: record.grantId,
65
+ agentId: record.agentId,
66
+ tenantId: record.tenantId,
67
+ notAfter: record.notAfter,
68
+ // exactOptionalPropertyTypes: omit the property entirely when absent.
69
+ ...(record.personId !== undefined ? { personId: asPersonId(record.personId) } : {}),
70
+ };
71
+ }
72
+ }
73
+ //# sourceMappingURL=in-memory-grant-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory-grant-resolver.js","sourceRoot":"","sources":["../../src/auth/in-memory-grant-resolver.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,6DAA6D;AAC7D,EAAE;AACF,iGAAiG;AACjG,iFAAiF;AACjF,wFAAwF;AACxF,uFAAuF;AACvF,oFAAoF;AACpF,EAAE;AACF,yDAAyD;AACzD,2FAA2F;AAC3F,EAAE;AACF,+FAA+F;AAC/F,oEAAoE;AACpE,yEAAyE;AAGzE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAa7C,uDAAuD;AACvD,MAAM,OAAO,qBAAqB;IACf,OAAO,CAAqC;IAC5C,MAAM,CAAW;IACjB,SAAS,CAAoB;IAE9C,YACE,MAAkC,EAClC,KAAa,EACb,IAAqC;QAErC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAuB,CAAC;QAC3C,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACxB,CAAC;QACD,IAAI,CAAC,OAAO,GAAK,GAAG,CAAC;QACrB,IAAI,CAAC,MAAM,GAAM,KAAK,CAAC;QACvB,IAAI,CAAC,SAAS,GAAG,IAAI,EAAE,QAAQ,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,kBAAkB,CACtB,OAAgB,EAChB,OAAgB,EAChB,QAAgB;QAEhB,gEAAgE;QAChE,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACjC,MAAM,IAAI,CAAC,SAAS,CAAC;QACvB,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAEzC,uDAAuD;QACvD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,0DAA0D;QAC1D,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;YAC/B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,4DAA4D;QAC5D,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACjC,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,uEAAuE;QACvE,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAChC,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;QAE9B,mCAAmC;QACnC,IAAI,GAAG,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;YAC3B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,8DAA8D;QAC9D,IAAI,GAAG,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC3B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,uEAAuE;QACvE,OAAO;YACL,OAAO,EAAG,MAAM,CAAC,OAAO;YACxB,OAAO,EAAG,MAAM,CAAC,OAAO;YACxB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,sEAAsE;YACtE,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACpF,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,16 @@
1
+ import type { TenantBoundary } from "./types.js";
2
+ /** Membership record for InMemoryTenantBoundary. */
3
+ export interface TenantMembership {
4
+ readonly tenantId: string;
5
+ readonly personId: string;
6
+ readonly status: "active" | "suspended";
7
+ }
8
+ /** @remarks TEST/DEV ONLY — not for production use. */
9
+ export declare class InMemoryTenantBoundary implements TenantBoundary {
10
+ private readonly _memberships;
11
+ private readonly _failWith;
12
+ constructor(memberships: ReadonlyArray<TenantMembership>, opts?: {
13
+ readonly failWith?: Error;
14
+ });
15
+ assertOwnership(tenantId: string, personId: string): Promise<void>;
16
+ }
@@ -0,0 +1,37 @@
1
+ // TEST/DEV ONLY — InMemoryTenantBoundary (Production-02a).
2
+ // Do NOT export from src/index.ts. Do NOT use in production.
3
+ //
4
+ // AUTH_TENANT_OWNERSHIP_CHECKED_001: assertOwnership fires before every OwnerContext mint.
5
+ // AUTH_TENANT_OWNERSHIP_DENIED_001: thrown for missing, suspended, or tenant-mismatch.
6
+ // AUTH_TENANT_SUBJECT_NOT_FOUND_SAFE_001: missing and suspended produce identical error
7
+ // — caller cannot distinguish non-existence from suspension (no enumeration leak).
8
+ //
9
+ // Storage-failure behavior: if constructed with failWith, assertOwnership throws that error
10
+ // rather than returning or falling through — storage errors must not masquerade as denials.
11
+ /** @remarks TEST/DEV ONLY — not for production use. */
12
+ export class InMemoryTenantBoundary {
13
+ _memberships;
14
+ _failWith;
15
+ constructor(memberships, opts) {
16
+ const map = new Map();
17
+ for (const m of memberships) {
18
+ map.set(`${m.tenantId}:${m.personId}`, m);
19
+ }
20
+ this._memberships = map;
21
+ this._failWith = opts?.failWith;
22
+ }
23
+ async assertOwnership(tenantId, personId) {
24
+ // AUTH_TENANT_OWNERSHIP_CHECKED_001: always fires; never falls through on error
25
+ if (this._failWith !== undefined) {
26
+ throw this._failWith;
27
+ }
28
+ const record = this._memberships.get(`${tenantId}:${personId}`);
29
+ // AUTH_TENANT_SUBJECT_NOT_FOUND_SAFE_001:
30
+ // Missing and suspended produce the same code — no enumeration leak.
31
+ if (record === undefined || record.status !== "active") {
32
+ throw Object.assign(new Error("AUTH_TENANT_OWNERSHIP_DENIED_001: subject is not an active member of the tenant"), { code: "AUTH_TENANT_OWNERSHIP_DENIED_001" });
33
+ }
34
+ // AUTH_TENANT_OWNERSHIP_CHECKED_001: resolves void on success
35
+ }
36
+ }
37
+ //# sourceMappingURL=in-memory-tenant-boundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory-tenant-boundary.js","sourceRoot":"","sources":["../../src/auth/in-memory-tenant-boundary.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,6DAA6D;AAC7D,EAAE;AACF,2FAA2F;AAC3F,uFAAuF;AACvF,wFAAwF;AACxF,qFAAqF;AACrF,EAAE;AACF,4FAA4F;AAC5F,8FAA8F;AAW9F,uDAAuD;AACvD,MAAM,OAAO,sBAAsB;IAChB,YAAY,CAAwC;IACpD,SAAS,CAAuB;IAEjD,YACE,WAA4C,EAC5C,IAAoC;QAEpC,MAAM,GAAG,GAAG,IAAI,GAAG,EAA4B,CAAC;QAChD,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;YAC5B,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;QAC5C,CAAC;QACD,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC;QACxB,IAAI,CAAC,SAAS,GAAO,IAAI,EAAE,QAAQ,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,QAAgB,EAAE,QAAgB;QACtD,gFAAgF;QAChF,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACjC,MAAM,IAAI,CAAC,SAAS,CAAC;QACvB,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,QAAQ,IAAI,QAAQ,EAAE,CAAC,CAAC;QAEhE,0CAA0C;QAC1C,qEAAqE;QACrE,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACvD,MAAM,MAAM,CAAC,MAAM,CACjB,IAAI,KAAK,CACP,iFAAiF,CAClF,EACD,EAAE,IAAI,EAAE,kCAAkC,EAAE,CAC7C,CAAC;QACJ,CAAC;QACD,8DAA8D;IAChE,CAAC;CACF"}
@@ -0,0 +1,38 @@
1
+ import type { AuthAdapter, VerifiedPrincipal } from "./types.js";
2
+ import type { HttpClient } from "./http-client.js";
3
+ /**
4
+ * Host-supplied synchronous function that maps a validated external JWT payload
5
+ * to a VerifiedPrincipal. Called only after signature, exp, nbf, iss, aud verified.
6
+ * Must throw if mapping is not possible; error is caught and re-thrown as
7
+ * EXTERNAL_IDP_CLAIM_MAPPER_THREW_001 (original message is NOT propagated).
8
+ */
9
+ export type ClaimMapper = (payload: Record<string, unknown>) => VerifiedPrincipal;
10
+ export interface JwksKeyRingAuthAdapterConfig {
11
+ /** Full HTTPS URL to the JWKS endpoint. */
12
+ readonly jwksUri: string;
13
+ /** Expected iss claim — exact string match required. */
14
+ readonly issuer: string;
15
+ /** Expected aud claim — string or array; at least one must appear in JWT aud. */
16
+ readonly audience: string | readonly string[];
17
+ /** Signing algorithm. Only "RS256" is valid. */
18
+ readonly algorithm: "RS256";
19
+ /** Host-supplied claim mapper. Must not be async. */
20
+ readonly claimMapper: ClaimMapper;
21
+ /** HTTP client used for JWKS fetch. Host-supplied — no default bundled. */
22
+ readonly httpClient: HttpClient;
23
+ /** JWKS cache TTL in seconds. Default: 3600. */
24
+ readonly cacheTtlSeconds?: number;
25
+ /** JWKS fetch timeout in milliseconds. Default: 5000. */
26
+ readonly fetchTimeoutMs?: number;
27
+ /** Maximum JWKS response size in bytes. Default: 65536. */
28
+ readonly maxJwksByteSize?: number;
29
+ }
30
+ export declare class JwksKeyRingAuthAdapter implements AuthAdapter {
31
+ private readonly _issuer;
32
+ private readonly _audience;
33
+ private readonly _mapper;
34
+ private readonly _ring;
35
+ constructor(config: JwksKeyRingAuthAdapterConfig);
36
+ verify(credential: string): Promise<VerifiedPrincipal>;
37
+ private _validateAudience;
38
+ }
@@ -0,0 +1,123 @@
1
+ // src/auth/jwks-key-ring-auth-adapter.ts
2
+ // INTERNAL — not exported from src/index.ts.
3
+ // RS256 AuthAdapter backed by a live JWKS endpoint.
4
+ // Algorithm: RS256 only. alg:none always rejected. kid required.
5
+ //
6
+ // CRYPTO_BOUNDARY_ONLY_001: does NOT import crypto primitives at runtime.
7
+ // All crypto is delegated to jwt-core.ts (verifyRsaSig).
8
+ //
9
+ // AUTH_FACTORY_AUTH_ADAPTER_OPAQUE_001: the production factory does not
10
+ // import this file. It is the host's responsibility to construct and inject.
11
+ //
12
+ // See docs/specs/post-mvp-external-idp-oidc-spec.md §7
13
+ // KeyObject not imported here — verifyRsaSig is called with inferred type from jwt-core
14
+ import { JwksKeyRing } from "./jwks-key-ring.js";
15
+ import { throwErr, splitJwt, parseJsonObject, verifyRsaSig, } from "./jwt-core.js";
16
+ // ─── JwksKeyRingAuthAdapter ───────────────────────────────────────────────────
17
+ export class JwksKeyRingAuthAdapter {
18
+ _issuer;
19
+ _audience;
20
+ _mapper;
21
+ _ring;
22
+ constructor(config) {
23
+ // Construction-time validation
24
+ if (config.algorithm !== "RS256") {
25
+ throwErr("EXTERNAL_IDP_ALG_UNSUPPORTED_001", "only RS256 is supported");
26
+ }
27
+ if (!config.jwksUri.startsWith("https://")) {
28
+ throwErr("EXTERNAL_IDP_JWKS_URI_INSECURE_001", "jwksUri must use HTTPS");
29
+ }
30
+ if (!config.issuer) {
31
+ throwErr("EXTERNAL_IDP_ISSUER_EMPTY_001", "issuer must be a non-empty string");
32
+ }
33
+ const aud = Array.isArray(config.audience) ? config.audience : [config.audience];
34
+ if (aud.length === 0 || (aud.length === 1 && aud[0] === "")) {
35
+ throwErr("EXTERNAL_IDP_AUDIENCE_EMPTY_001", "audience must be a non-empty value");
36
+ }
37
+ this._issuer = config.issuer;
38
+ this._audience = aud;
39
+ this._mapper = config.claimMapper;
40
+ this._ring = new JwksKeyRing({
41
+ jwksUri: config.jwksUri,
42
+ httpClient: config.httpClient,
43
+ cacheTtlMs: (config.cacheTtlSeconds ?? 3600) * 1000,
44
+ fetchTimeoutMs: config.fetchTimeoutMs ?? 5000,
45
+ maxBytes: config.maxJwksByteSize ?? 65536,
46
+ });
47
+ }
48
+ async verify(credential) {
49
+ // Step 1 — Split JWT into exactly 3 segments
50
+ const [rawHeader, rawPayload, rawSig] = splitJwt(credential);
51
+ // Step 2 — Decode and parse header
52
+ const h = parseJsonObject(rawHeader, "header");
53
+ // Step 3 — Algorithm enforcement: alg:none always rejected; then binding check
54
+ const alg = h["alg"];
55
+ if (typeof alg === "string" && alg.toLowerCase() === "none") {
56
+ throwErr("EXTERNAL_IDP_ALG_NONE_REJECTED_001", "alg:none is not accepted");
57
+ }
58
+ if (alg !== "RS256") {
59
+ throwErr("EXTERNAL_IDP_ALG_MISMATCH_001", "unsupported algorithm; RS256 required");
60
+ }
61
+ // Step 4 — kid required
62
+ const kid = h["kid"];
63
+ if (typeof kid !== "string" || kid.length === 0) {
64
+ throwErr("EXTERNAL_IDP_KID_REQUIRED_001", "kid header is required and must be a non-empty string");
65
+ }
66
+ // Step 5 — Resolve public key (at-most-one JWKS fetch per verify call)
67
+ const publicKey = await this._ring.getPublicKey(kid);
68
+ if (publicKey === undefined) {
69
+ throwErr("EXTERNAL_IDP_KID_NOT_FOUND_001", "kid not found in JWKS");
70
+ }
71
+ // Step 6 — RS256 signature verification (delegates to jwt-core; no crypto import here)
72
+ verifyRsaSig(rawSig, rawHeader + "." + rawPayload, publicKey);
73
+ // Step 7 — Decode and parse payload
74
+ const p = parseJsonObject(rawPayload, "payload");
75
+ // Step 8 — Validate iss (exact match)
76
+ if (p["iss"] !== this._issuer) {
77
+ throwErr("EXTERNAL_IDP_ISSUER_MISMATCH_001", "iss does not match expected issuer");
78
+ }
79
+ // Step 9 — Validate aud
80
+ this._validateAudience(p["aud"]);
81
+ // Step 10 — Validate exp
82
+ const expRaw = p["exp"];
83
+ if (typeof expRaw !== "number") {
84
+ throwErr("EXTERNAL_IDP_TOKEN_EXPIRED_001", "exp claim is absent or not a number");
85
+ }
86
+ if (expRaw * 1000 <= Date.now()) {
87
+ throwErr("EXTERNAL_IDP_TOKEN_EXPIRED_001", "token has expired");
88
+ }
89
+ // Step 11 — Validate nbf (if present)
90
+ if ("nbf" in p) {
91
+ const nbfRaw = p["nbf"];
92
+ if (typeof nbfRaw !== "number") {
93
+ throwErr("EXTERNAL_IDP_TOKEN_NOT_YET_VALID_001", "nbf claim is not a number");
94
+ }
95
+ if (nbfRaw * 1000 > Date.now()) {
96
+ throwErr("EXTERNAL_IDP_TOKEN_NOT_YET_VALID_001", "token is not yet valid");
97
+ }
98
+ }
99
+ // Step 12 — Claim mapping (host-supplied; error message must not propagate)
100
+ let principal;
101
+ try {
102
+ principal = this._mapper(p);
103
+ }
104
+ catch {
105
+ throwErr("EXTERNAL_IDP_CLAIM_MAPPER_THREW_001", "claim mapper failed");
106
+ }
107
+ return principal;
108
+ }
109
+ // ─── Private helpers ──────────────────────────────────────────────────────
110
+ _validateAudience(jwtAud) {
111
+ const jwtAudArr = typeof jwtAud === "string" ? [jwtAud] :
112
+ Array.isArray(jwtAud) ? jwtAud :
113
+ [];
114
+ if (jwtAudArr.length === 0) {
115
+ throwErr("EXTERNAL_IDP_AUDIENCE_MISMATCH_001", "aud claim is absent or invalid");
116
+ }
117
+ const hasMatch = this._audience.some(a => jwtAudArr.includes(a));
118
+ if (!hasMatch) {
119
+ throwErr("EXTERNAL_IDP_AUDIENCE_MISMATCH_001", "aud does not include a configured audience value");
120
+ }
121
+ }
122
+ }
123
+ //# sourceMappingURL=jwks-key-ring-auth-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwks-key-ring-auth-adapter.js","sourceRoot":"","sources":["../../src/auth/jwks-key-ring-auth-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { KeyObject } from "./jwt-core.js";
2
+ import type { HttpClient } from "./http-client.js";
3
+ export interface JwksKeyRingConfig {
4
+ readonly jwksUri: string;
5
+ readonly httpClient: HttpClient;
6
+ readonly cacheTtlMs: number;
7
+ readonly fetchTimeoutMs: number;
8
+ readonly maxBytes: number;
9
+ }
10
+ export declare class JwksKeyRing {
11
+ private readonly _config;
12
+ private _cache;
13
+ constructor(config: JwksKeyRingConfig);
14
+ /**
15
+ * Resolve the public key for the given kid.
16
+ * Cache hit (within TTL): returns immediately.
17
+ * Cache miss / expired: triggers at-most-one JWKS fetch, then looks up again.
18
+ * Returns undefined if kid is not found after fetch.
19
+ * Throws EXTERNAL_IDP_JWKS_FETCH_FAILED_001 or EXTERNAL_IDP_JWKS_PARSE_ERROR_001 on error.
20
+ */
21
+ getPublicKey(kid: string): Promise<KeyObject | undefined>;
22
+ /** Force cache invalidation and re-fetch. */
23
+ forceRefresh(): Promise<void>;
24
+ private _fetchAndPopulate;
25
+ }
@@ -0,0 +1,111 @@
1
+ // src/auth/jwks-key-ring.ts
2
+ // INTERNAL — not exported from src/index.ts.
3
+ // In-memory JWKS key cache with TTL and at-most-one-fetch-per-call semantics.
4
+ //
5
+ // CRYPTO_BOUNDARY_ONLY_001: does NOT import crypto primitives at runtime.
6
+ // All RSA key operations are delegated to importRsaPublicKey() in jwt-core.ts.
7
+ //
8
+ // See docs/specs/post-mvp-external-idp-oidc-spec.md §7.3, §7.4
9
+ import { importRsaPublicKey, throwErr } from "./jwt-core.js";
10
+ // ─── JwksKeyRing ─────────────────────────────────────────────────────────────
11
+ export class JwksKeyRing {
12
+ _config;
13
+ _cache;
14
+ constructor(config) {
15
+ this._config = config;
16
+ this._cache = new Map();
17
+ }
18
+ /**
19
+ * Resolve the public key for the given kid.
20
+ * Cache hit (within TTL): returns immediately.
21
+ * Cache miss / expired: triggers at-most-one JWKS fetch, then looks up again.
22
+ * Returns undefined if kid is not found after fetch.
23
+ * Throws EXTERNAL_IDP_JWKS_FETCH_FAILED_001 or EXTERNAL_IDP_JWKS_PARSE_ERROR_001 on error.
24
+ */
25
+ async getPublicKey(kid) {
26
+ const now = Date.now();
27
+ const cached = this._cache.get(kid);
28
+ if (cached !== undefined && cached.expiresAtMs > now) {
29
+ return cached.publicKey;
30
+ }
31
+ // Cache miss or expired — fetch once
32
+ await this._fetchAndPopulate();
33
+ const fresh = this._cache.get(kid);
34
+ if (fresh !== undefined && fresh.expiresAtMs > now) {
35
+ return fresh.publicKey;
36
+ }
37
+ return undefined;
38
+ }
39
+ /** Force cache invalidation and re-fetch. */
40
+ async forceRefresh() {
41
+ this._cache.clear();
42
+ await this._fetchAndPopulate();
43
+ }
44
+ // ─── Private ───────────────────────────────────────────────────────────────
45
+ async _fetchAndPopulate() {
46
+ const { jwksUri, httpClient, fetchTimeoutMs, maxBytes, cacheTtlMs } = this._config;
47
+ // Fetch JWKS endpoint
48
+ let responseText;
49
+ try {
50
+ responseText = await httpClient.getText(jwksUri, { timeoutMs: fetchTimeoutMs, maxBytes });
51
+ }
52
+ catch {
53
+ throwErr("EXTERNAL_IDP_JWKS_FETCH_FAILED_001", "JWKS fetch failed");
54
+ }
55
+ // Belt-and-suspenders size guard (in case client doesn't enforce maxBytes)
56
+ if (responseText.length > maxBytes) {
57
+ throwErr("EXTERNAL_IDP_JWKS_FETCH_FAILED_001", "JWKS response exceeds maximum allowed size");
58
+ }
59
+ // Parse JSON
60
+ let parsed;
61
+ try {
62
+ parsed = JSON.parse(responseText);
63
+ }
64
+ catch {
65
+ throwErr("EXTERNAL_IDP_JWKS_PARSE_ERROR_001", "JWKS response is not valid JSON");
66
+ }
67
+ if (typeof parsed !== "object" || parsed === null || !("keys" in parsed)) {
68
+ throwErr("EXTERNAL_IDP_JWKS_PARSE_ERROR_001", "JWKS response missing keys field");
69
+ }
70
+ const keysField = parsed.keys;
71
+ if (!Array.isArray(keysField)) {
72
+ throwErr("EXTERNAL_IDP_JWKS_PARSE_ERROR_001", "JWKS keys is not an array");
73
+ }
74
+ const expiresAtMs = Date.now() + cacheTtlMs;
75
+ let imported = 0;
76
+ for (const raw of keysField) {
77
+ if (typeof raw !== "object" || raw === null)
78
+ continue;
79
+ const entry = raw;
80
+ // Only RSA signing keys
81
+ if (entry["kty"] !== "RSA")
82
+ continue;
83
+ const useField = entry["use"];
84
+ if (useField !== undefined && useField !== "sig")
85
+ continue;
86
+ const kidField = entry["kid"];
87
+ if (typeof kidField !== "string" || kidField.length === 0)
88
+ continue;
89
+ // Delegate RSA key import to jwt-core (CRYPTO_BOUNDARY_ONLY_001)
90
+ // importRsaPublicKey throws on malformed or weak keys — catch and skip.
91
+ let publicKey;
92
+ try {
93
+ publicKey = importRsaPublicKey(entry);
94
+ }
95
+ catch (err) {
96
+ // Re-throw EXTERNAL_IDP_PUBLIC_KEY_TOO_SMALL_001 (security invariant) but skip parse errors.
97
+ if (err instanceof Error &&
98
+ err.code === "EXTERNAL_IDP_PUBLIC_KEY_TOO_SMALL_001") {
99
+ throw err;
100
+ }
101
+ continue; // skip malformed entries; keep trying remaining keys
102
+ }
103
+ this._cache.set(kidField, { publicKey, expiresAtMs });
104
+ imported++;
105
+ }
106
+ if (imported === 0) {
107
+ throwErr("EXTERNAL_IDP_JWKS_PARSE_ERROR_001", "JWKS contains no valid RSA signing keys");
108
+ }
109
+ }
110
+ }
111
+ //# sourceMappingURL=jwks-key-ring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwks-key-ring.js","sourceRoot":"","sources":["../../src/auth/jwks-key-ring.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { AuthAdapter, Clock, VerifiedPrincipal } from "./types.js";
2
+ export interface JwtAuthAdapterConfig {
3
+ readonly secret: Uint8Array;
4
+ readonly expectedIssuer: string;
5
+ readonly expectedAudience: string;
6
+ readonly clock?: Clock;
7
+ }
8
+ export declare class JwtAuthAdapter implements AuthAdapter {
9
+ private readonly _secret;
10
+ private readonly _issuer;
11
+ private readonly _audience;
12
+ private readonly _clock;
13
+ constructor(config: JwtAuthAdapterConfig);
14
+ verify(credential: string): Promise<VerifiedPrincipal>;
15
+ }
@@ -0,0 +1,59 @@
1
+ // src/auth/jwt-auth-adapter.ts
2
+ // TEST/DEV in Production-03-a — NOT exported from src/index.ts.
3
+ // Single-secret HS256 adapter. Cryptographic helpers live in jwt-core.ts
4
+ // (the credential-verification crypto boundary).
5
+ //
6
+ // AUTH_CREDENTIAL_KEY_CONFIG_INVALID_001 ✅ Production-03-a
7
+ // AUTH_CREDENTIAL_MALFORMED_001 ✅ Production-03-a
8
+ // AUTH_CREDENTIAL_ALGORITHM_UNSUPPORTED_001 ✅ Production-03-a
9
+ // AUTH_CREDENTIAL_KEY_ID_UNKNOWN_001 ✅ Production-03-a
10
+ // AUTH_CREDENTIAL_SIGNATURE_INVALID_001 ✅ Production-03-a
11
+ // AUTH_CREDENTIAL_TIME_CLAIM_INVALID_001 ✅ Production-03-a
12
+ // AUTH_CREDENTIAL_EXPIRED_001 ✅ Production-03-a
13
+ // AUTH_CREDENTIAL_NOT_YET_VALID_001 ✅ Production-03-a
14
+ // AUTH_CREDENTIAL_ISSUER_INVALID_001 ✅ Production-03-a
15
+ // AUTH_CREDENTIAL_AUDIENCE_INVALID_001 ✅ Production-03-a
16
+ // AUTH_CREDENTIAL_CLAIM_MISSING_001 ✅ Production-03-a
17
+ // AUTH_CREDENTIAL_CLAIM_INVALID_001 ✅ Production-03-a
18
+ import { throwErr, MIN_SECRET_BYTES, splitJwt, parseJsonObject, assertAlgHS256, verifyHmacSig, verifyPayloadAndBuildPrincipal, } from "./jwt-core.js";
19
+ export class JwtAuthAdapter {
20
+ _secret;
21
+ _issuer;
22
+ _audience;
23
+ _clock;
24
+ constructor(config) {
25
+ // C1 — construction-time guards (AUTH_CREDENTIAL_KEY_CONFIG_INVALID_001)
26
+ if (config.secret == null || config.secret.length < MIN_SECRET_BYTES) {
27
+ throwErr("AUTH_CREDENTIAL_KEY_CONFIG_INVALID_001", `secret must be a Uint8Array of at least ${MIN_SECRET_BYTES} bytes`);
28
+ }
29
+ if (!config.expectedIssuer) {
30
+ throwErr("AUTH_CREDENTIAL_KEY_CONFIG_INVALID_001", "expectedIssuer must be a non-empty string");
31
+ }
32
+ if (!config.expectedAudience) {
33
+ throwErr("AUTH_CREDENTIAL_KEY_CONFIG_INVALID_001", "expectedAudience must be a non-empty string");
34
+ }
35
+ this._secret = Buffer.from(config.secret);
36
+ this._issuer = config.expectedIssuer;
37
+ this._audience = config.expectedAudience;
38
+ this._clock = config.clock ?? { now: () => new Date().toISOString() };
39
+ }
40
+ async verify(credential) {
41
+ const nowMs = Date.parse(this._clock.now());
42
+ // Step 1 — structure: exactly 3 non-empty segments
43
+ const [rawHeader, rawPayload, rawSig] = splitJwt(credential);
44
+ // Steps 2-3 — header: base64url decode + JSON parse
45
+ const h = parseJsonObject(rawHeader, "header");
46
+ // Step 4 — algorithm enforcement
47
+ assertAlgHS256(h);
48
+ // Step 5 — kid rejection (single-key mode never accepts a kid-bearing token)
49
+ if ("kid" in h) {
50
+ throwErr("AUTH_CREDENTIAL_KEY_ID_UNKNOWN_001", "token contains kid; multi-key is not supported in single-secret mode");
51
+ }
52
+ // Steps 6-8 — HMAC-SHA256 with timingSafeEqual (via jwt-core.ts)
53
+ verifyHmacSig(rawHeader, rawPayload, rawSig, this._secret);
54
+ // Steps 9-21 — payload parse + full verification + VerifiedPrincipal assembly
55
+ const p = parseJsonObject(rawPayload, "payload");
56
+ return verifyPayloadAndBuildPrincipal(p, nowMs, this._issuer, this._audience);
57
+ }
58
+ }
59
+ //# sourceMappingURL=jwt-auth-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt-auth-adapter.js","sourceRoot":"","sources":["../../src/auth/jwt-auth-adapter.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,gEAAgE;AAChE,yEAAyE;AACzE,iDAAiD;AACjD,EAAE;AACF,8DAA8D;AAC9D,8DAA8D;AAC9D,8DAA8D;AAC9D,8DAA8D;AAC9D,8DAA8D;AAC9D,8DAA8D;AAC9D,8DAA8D;AAC9D,8DAA8D;AAC9D,8DAA8D;AAC9D,8DAA8D;AAC9D,8DAA8D;AAC9D,8DAA8D;AAG9D,OAAO,EACL,QAAQ,EACR,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,cAAc,EACd,aAAa,EACb,8BAA8B,GAC/B,MAAM,eAAe,CAAC;AAWvB,MAAM,OAAO,cAAc;IACR,OAAO,CAAW;IAClB,OAAO,CAAW;IAClB,SAAS,CAAS;IAClB,MAAM,CAAW;IAElC,YAAY,MAA4B;QACtC,yEAAyE;QACzE,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;YACrE,QAAQ,CACN,wCAAwC,EACxC,2CAA2C,gBAAgB,QAAQ,CACpE,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;YAC3B,QAAQ,CAAC,wCAAwC,EAAE,2CAA2C,CAAC,CAAC;QAClG,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC7B,QAAQ,CAAC,wCAAwC,EAAE,6CAA6C,CAAC,CAAC;QACpG,CAAC;QACD,IAAI,CAAC,OAAO,GAAK,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO,GAAK,MAAM,CAAC,cAAc,CAAC;QACvC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,gBAAgB,CAAC;QACzC,IAAI,CAAC,MAAM,GAAM,MAAM,CAAC,KAAK,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;IAC3E,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,UAAkB;QAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;QAE5C,mDAAmD;QACnD,MAAM,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;QAE7D,oDAAoD;QACpD,MAAM,CAAC,GAAG,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAE/C,iCAAiC;QACjC,cAAc,CAAC,CAAC,CAAC,CAAC;QAElB,6EAA6E;QAC7E,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACf,QAAQ,CACN,oCAAoC,EACpC,sEAAsE,CACvE,CAAC;QACJ,CAAC;QAED,iEAAiE;QACjE,aAAa,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAE3D,8EAA8E;QAC9E,MAAM,CAAC,GAAG,eAAe,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QACjD,OAAO,8BAA8B,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAChF,CAAC;CACF"}
@@ -0,0 +1,32 @@
1
+ import type { KeyObject } from "node:crypto";
2
+ export type { KeyObject };
3
+ import type { VerifiedPrincipal } from "./types.js";
4
+ export declare const MIN_SECRET_BYTES = 32;
5
+ export declare const VALID_PRINCIPAL_TYPES: Set<string>;
6
+ export declare const VALID_ROLES: Set<string>;
7
+ export declare function throwErr(code: string, detail: string): never;
8
+ export declare function decodeB64url(segment: string): Buffer;
9
+ export declare function parseJsonObject(segment: string, label: string): Record<string, unknown>;
10
+ export declare function splitJwt(credential: string): [string, string, string];
11
+ export declare function assertAlgHS256(h: Record<string, unknown>): void;
12
+ export declare function verifyHmacSig(rawHeader: string, rawPayload: string, rawSig: string, secret: Buffer): void;
13
+ export declare function requireStr(p: Record<string, unknown>, name: string): string;
14
+ export declare function requireStrArr(p: Record<string, unknown>, name: string, valid: ReadonlySet<string> | null): ReadonlyArray<string>;
15
+ export declare function verifyPayloadAndBuildPrincipal(p: Record<string, unknown>, nowMs: number, expectedIssuer: string, expectedAudience: string): VerifiedPrincipal;
16
+ /**
17
+ * Import an RSA public key from a JWK entry (Record<string, unknown>).
18
+ * Enforces: kty must be "RSA", minimum 2048-bit modulus.
19
+ * Throws EXTERNAL_IDP_PUBLIC_KEY_TOO_SMALL_001 for weak keys.
20
+ * Throws EXTERNAL_IDP_JWKS_PARSE_ERROR_001 for malformed entries.
21
+ * Called only from JwksKeyRing — never from the auth adapter directly.
22
+ */
23
+ export declare function importRsaPublicKey(entry: Record<string, unknown>): KeyObject;
24
+ /**
25
+ * Verify an RS256 (PKCS#1 v1.5 / SHA-256) JWT signature.
26
+ * rawSig: base64url-encoded signature (JWT third segment).
27
+ * headerDotPayload: raw signing input ("header.payload", as sent over wire).
28
+ * publicKey: RSA KeyObject returned by importRsaPublicKey.
29
+ * Throws EXTERNAL_IDP_SIGNATURE_INVALID_001 on any verification failure.
30
+ * No sensitive data in error messages.
31
+ */
32
+ export declare function verifyRsaSig(rawSig: string, headerDotPayload: string, publicKey: KeyObject): void;