@opensyber/tokenforge 0.1.2 → 1.1.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 (184) hide show
  1. package/LICENSE.md +32 -0
  2. package/README.md +329 -52
  3. package/dist/adapters/astro.d.ts +64 -0
  4. package/dist/adapters/astro.d.ts.map +1 -0
  5. package/dist/adapters/astro.js +104 -0
  6. package/dist/adapters/astro.js.map +1 -0
  7. package/dist/adapters/cloud-v2-bridge.d.ts +8 -0
  8. package/dist/adapters/cloud-v2-bridge.d.ts.map +1 -0
  9. package/dist/adapters/cloud-v2-bridge.js +24 -0
  10. package/dist/adapters/cloud-v2-bridge.js.map +1 -0
  11. package/dist/adapters/express.d.ts +30 -2
  12. package/dist/adapters/express.d.ts.map +1 -1
  13. package/dist/adapters/express.js +65 -0
  14. package/dist/adapters/express.js.map +1 -1
  15. package/dist/adapters/fastify.d.ts +32 -6
  16. package/dist/adapters/fastify.d.ts.map +1 -1
  17. package/dist/adapters/fastify.js +65 -0
  18. package/dist/adapters/fastify.js.map +1 -1
  19. package/dist/adapters/hono.d.ts +13 -1
  20. package/dist/adapters/hono.d.ts.map +1 -1
  21. package/dist/adapters/hono.js +14 -1
  22. package/dist/adapters/hono.js.map +1 -1
  23. package/dist/adapters/index.d.ts +5 -3
  24. package/dist/adapters/index.d.ts.map +1 -1
  25. package/dist/adapters/index.js +5 -3
  26. package/dist/adapters/index.js.map +1 -1
  27. package/dist/adapters/nextjs.d.ts +31 -0
  28. package/dist/adapters/nextjs.d.ts.map +1 -1
  29. package/dist/adapters/nextjs.js +83 -30
  30. package/dist/adapters/nextjs.js.map +1 -1
  31. package/dist/adapters/sveltekit.d.ts +66 -0
  32. package/dist/adapters/sveltekit.d.ts.map +1 -0
  33. package/dist/adapters/sveltekit.js +101 -0
  34. package/dist/adapters/sveltekit.js.map +1 -0
  35. package/dist/client/action-signer.d.ts +53 -0
  36. package/dist/client/action-signer.d.ts.map +1 -0
  37. package/dist/client/action-signer.js +48 -0
  38. package/dist/client/action-signer.js.map +1 -0
  39. package/dist/client/binding.d.ts +24 -7
  40. package/dist/client/binding.d.ts.map +1 -1
  41. package/dist/client/binding.js +17 -6
  42. package/dist/client/binding.js.map +1 -1
  43. package/dist/client/cloud-device.d.ts +54 -0
  44. package/dist/client/cloud-device.d.ts.map +1 -0
  45. package/dist/client/cloud-device.js +301 -0
  46. package/dist/client/cloud-device.js.map +1 -0
  47. package/dist/client/crypto.d.ts.map +1 -1
  48. package/dist/client/crypto.js.map +1 -1
  49. package/dist/client/index.d.ts +14 -0
  50. package/dist/client/index.d.ts.map +1 -1
  51. package/dist/client/index.js +30 -2
  52. package/dist/client/index.js.map +1 -1
  53. package/dist/client/interceptor.d.ts.map +1 -1
  54. package/dist/client/interceptor.js +27 -3
  55. package/dist/client/interceptor.js.map +1 -1
  56. package/dist/client/signer.d.ts +40 -3
  57. package/dist/client/signer.d.ts.map +1 -1
  58. package/dist/client/signer.js +56 -5
  59. package/dist/client/signer.js.map +1 -1
  60. package/dist/client/storage.d.ts.map +1 -1
  61. package/dist/client/storage.js.map +1 -1
  62. package/dist/client/webauthn.d.ts +45 -0
  63. package/dist/client/webauthn.d.ts.map +1 -0
  64. package/dist/client/webauthn.js +118 -0
  65. package/dist/client/webauthn.js.map +1 -0
  66. package/dist/protocol/v2.d.ts +23 -0
  67. package/dist/protocol/v2.d.ts.map +1 -0
  68. package/dist/protocol/v2.js +157 -0
  69. package/dist/protocol/v2.js.map +1 -0
  70. package/dist/react/index.d.ts.map +1 -1
  71. package/dist/react/index.js.map +1 -1
  72. package/dist/react/provider.d.ts.map +1 -1
  73. package/dist/react/provider.js.map +1 -1
  74. package/dist/server/action-verify.d.ts +56 -0
  75. package/dist/server/action-verify.d.ts.map +1 -0
  76. package/dist/server/action-verify.js +47 -0
  77. package/dist/server/action-verify.js.map +1 -0
  78. package/dist/server/activity-histogram.d.ts +47 -0
  79. package/dist/server/activity-histogram.d.ts.map +1 -0
  80. package/dist/server/activity-histogram.js +76 -0
  81. package/dist/server/activity-histogram.js.map +1 -0
  82. package/dist/server/aitm-heuristics.d.ts +57 -0
  83. package/dist/server/aitm-heuristics.d.ts.map +1 -0
  84. package/dist/server/aitm-heuristics.js +120 -0
  85. package/dist/server/aitm-heuristics.js.map +1 -0
  86. package/dist/server/bind-with-id-token.d.ts +55 -0
  87. package/dist/server/bind-with-id-token.d.ts.map +1 -0
  88. package/dist/server/bind-with-id-token.js +70 -0
  89. package/dist/server/bind-with-id-token.js.map +1 -0
  90. package/dist/server/bound-cookie.d.ts +39 -0
  91. package/dist/server/bound-cookie.d.ts.map +1 -0
  92. package/dist/server/bound-cookie.js +59 -0
  93. package/dist/server/bound-cookie.js.map +1 -0
  94. package/dist/server/cbor.d.ts +20 -0
  95. package/dist/server/cbor.d.ts.map +1 -0
  96. package/dist/server/cbor.js +110 -0
  97. package/dist/server/cbor.js.map +1 -0
  98. package/dist/server/cloud-bind.d.ts +42 -0
  99. package/dist/server/cloud-bind.d.ts.map +1 -0
  100. package/dist/server/cloud-bind.js +387 -0
  101. package/dist/server/cloud-bind.js.map +1 -0
  102. package/dist/server/cloud-middleware.d.ts +46 -0
  103. package/dist/server/cloud-middleware.d.ts.map +1 -0
  104. package/dist/server/cloud-middleware.js +344 -0
  105. package/dist/server/cloud-middleware.js.map +1 -0
  106. package/dist/server/cloud-verify.d.ts +30 -0
  107. package/dist/server/cloud-verify.d.ts.map +1 -0
  108. package/dist/server/cloud-verify.js +206 -0
  109. package/dist/server/cloud-verify.js.map +1 -0
  110. package/dist/server/dbsc-challenge.d.ts +51 -0
  111. package/dist/server/dbsc-challenge.d.ts.map +1 -0
  112. package/dist/server/dbsc-challenge.js +78 -0
  113. package/dist/server/dbsc-challenge.js.map +1 -0
  114. package/dist/server/dbsc-endpoint.d.ts +20 -0
  115. package/dist/server/dbsc-endpoint.d.ts.map +1 -0
  116. package/dist/server/dbsc-endpoint.js +85 -0
  117. package/dist/server/dbsc-endpoint.js.map +1 -0
  118. package/dist/server/dbsc-verify.d.ts +32 -0
  119. package/dist/server/dbsc-verify.d.ts.map +1 -0
  120. package/dist/server/dbsc-verify.js +65 -0
  121. package/dist/server/dbsc-verify.js.map +1 -0
  122. package/dist/server/identity-profile.d.ts +18 -0
  123. package/dist/server/identity-profile.d.ts.map +1 -0
  124. package/dist/server/identity-profile.js +113 -0
  125. package/dist/server/identity-profile.js.map +1 -0
  126. package/dist/server/index.d.ts +12 -0
  127. package/dist/server/index.d.ts.map +1 -1
  128. package/dist/server/index.js +7 -0
  129. package/dist/server/index.js.map +1 -1
  130. package/dist/server/jws-verify.d.ts +46 -0
  131. package/dist/server/jws-verify.d.ts.map +1 -0
  132. package/dist/server/jws-verify.js +64 -0
  133. package/dist/server/jws-verify.js.map +1 -0
  134. package/dist/server/middleware.d.ts +24 -0
  135. package/dist/server/middleware.d.ts.map +1 -1
  136. package/dist/server/middleware.js +87 -20
  137. package/dist/server/middleware.js.map +1 -1
  138. package/dist/server/oidc-verify.d.ts +71 -0
  139. package/dist/server/oidc-verify.d.ts.map +1 -0
  140. package/dist/server/oidc-verify.js +115 -0
  141. package/dist/server/oidc-verify.js.map +1 -0
  142. package/dist/server/policy.d.ts +47 -0
  143. package/dist/server/policy.d.ts.map +1 -0
  144. package/dist/server/policy.js +84 -0
  145. package/dist/server/policy.js.map +1 -0
  146. package/dist/server/step-up-policy.d.ts +36 -0
  147. package/dist/server/step-up-policy.d.ts.map +1 -0
  148. package/dist/server/step-up-policy.js +101 -0
  149. package/dist/server/step-up-policy.js.map +1 -0
  150. package/dist/server/storage/index.d.ts +3 -0
  151. package/dist/server/storage/index.d.ts.map +1 -0
  152. package/dist/server/storage/index.js +2 -0
  153. package/dist/server/storage/index.js.map +1 -0
  154. package/dist/server/storage/interface.d.ts +73 -0
  155. package/dist/server/storage/interface.d.ts.map +1 -0
  156. package/dist/server/storage/interface.js +2 -0
  157. package/dist/server/storage/interface.js.map +1 -0
  158. package/dist/server/storage/memory.d.ts +50 -0
  159. package/dist/server/storage/memory.d.ts.map +1 -0
  160. package/dist/server/storage/memory.js +121 -0
  161. package/dist/server/storage/memory.js.map +1 -0
  162. package/dist/server/webauthn-verify.d.ts +41 -0
  163. package/dist/server/webauthn-verify.d.ts.map +1 -0
  164. package/dist/server/webauthn-verify.js +175 -0
  165. package/dist/server/webauthn-verify.js.map +1 -0
  166. package/dist/server/webhooks.d.ts +42 -0
  167. package/dist/server/webhooks.d.ts.map +1 -0
  168. package/dist/server/webhooks.js +77 -0
  169. package/dist/server/webhooks.js.map +1 -0
  170. package/dist/shared/action-hash.d.ts +23 -0
  171. package/dist/shared/action-hash.d.ts.map +1 -0
  172. package/dist/shared/action-hash.js +35 -0
  173. package/dist/shared/action-hash.js.map +1 -0
  174. package/dist/shared/identity.d.ts +45 -0
  175. package/dist/shared/identity.d.ts.map +1 -0
  176. package/dist/shared/identity.js +176 -0
  177. package/dist/shared/identity.js.map +1 -0
  178. package/dist/shared/types-internal.d.ts.map +1 -1
  179. package/dist/shared/types-internal.js.map +1 -1
  180. package/dist/shared/types.d.ts +49 -2
  181. package/dist/shared/types.d.ts.map +1 -1
  182. package/dist/shared/types.js +1 -0
  183. package/dist/shared/types.js.map +1 -1
  184. package/package.json +31 -33
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dbsc-challenge.d.ts","sourceRoot":"../src/","sources":["server/dbsc-challenge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;CACvE;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,MAAM,EAAE,eAAe,CAAC;CACzB;AAED,wBAAsB,cAAc,CAClC,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAqB/B;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACtF,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAahF"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * DBSC challenge issuer — emits the random bytes the client must sign,
3
+ * stores their SHA-256 hash + a 60-second expiry, and consumes them
4
+ * exactly once.
5
+ *
6
+ * The challenge bytes never live at rest — we store only the hash so a
7
+ * DB read does not let an attacker pre-compute valid signatures.
8
+ *
9
+ * Pure logic; persistence is injected so this works on any storage.
10
+ */
11
+ const CHALLENGE_BYTES = 32;
12
+ const DEFAULT_TTL_SECONDS = 60;
13
+ export async function issueChallenge(store, input) {
14
+ const bytes = new Uint8Array(CHALLENGE_BYTES);
15
+ crypto.getRandomValues(bytes);
16
+ const challenge = bytesToBase64Url(bytes);
17
+ const challengeHash = await sha256Base64Url(bytes);
18
+ const ttl = input.ttlSeconds ?? DEFAULT_TTL_SECONDS;
19
+ const issued = new Date();
20
+ const expires = new Date(issued.getTime() + ttl * 1000);
21
+ const record = {
22
+ id: crypto.randomUUID(),
23
+ tenantId: input.tenantId,
24
+ challengeHash,
25
+ purpose: input.purpose,
26
+ sessionId: input.sessionId ?? null,
27
+ actionHash: input.actionHash ?? null,
28
+ issuedAt: issued.toISOString(),
29
+ expiresAt: expires.toISOString(),
30
+ consumed: false,
31
+ };
32
+ await store.put(record);
33
+ return { challenge, record };
34
+ }
35
+ export async function consumeChallenge(store, challenge, expected) {
36
+ const bytes = base64UrlToBytes(challenge);
37
+ if (!bytes)
38
+ return { ok: false, reason: 'malformed_challenge' };
39
+ const hash = await sha256Base64Url(bytes);
40
+ const found = await store.takeIfFresh(hash, new Date());
41
+ if (!found)
42
+ return { ok: false, reason: 'challenge_unknown_or_expired' };
43
+ if (found.consumed)
44
+ return { ok: false, reason: 'challenge_replay' };
45
+ if (found.tenantId !== expected.tenantId)
46
+ return { ok: false, reason: 'challenge_tenant_mismatch' };
47
+ if (found.purpose !== expected.purpose)
48
+ return { ok: false, reason: 'challenge_purpose_mismatch' };
49
+ if (expected.sessionId && found.sessionId !== expected.sessionId) {
50
+ return { ok: false, reason: 'challenge_session_mismatch' };
51
+ }
52
+ return { ok: true, record: found };
53
+ }
54
+ async function sha256Base64Url(bytes) {
55
+ const buf = await crypto.subtle.digest('SHA-256', bytes);
56
+ return bytesToBase64Url(new Uint8Array(buf));
57
+ }
58
+ function bytesToBase64Url(bytes) {
59
+ let bin = '';
60
+ for (let i = 0; i < bytes.length; i++)
61
+ bin += String.fromCharCode(bytes[i]);
62
+ return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
63
+ }
64
+ function base64UrlToBytes(b64) {
65
+ const s = b64.replace(/-/g, '+').replace(/_/g, '/');
66
+ const padded = s + '='.repeat((4 - (s.length % 4)) % 4);
67
+ try {
68
+ const bin = atob(padded);
69
+ const out = new Uint8Array(bin.length);
70
+ for (let i = 0; i < bin.length; i++)
71
+ out[i] = bin.charCodeAt(i);
72
+ return out;
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ //# sourceMappingURL=dbsc-challenge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dbsc-challenge.js","sourceRoot":"../src/","sources":["server/dbsc-challenge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * DBSC challenge endpoint — issues and consumes DBSC challenges.
3
+ * Mount at /api/tf/dbsc on the main Hono app alongside the binding routes.
4
+ */
5
+ import { Hono } from 'hono';
6
+ import type { TokenForgeServerOptions } from '../shared/types.js';
7
+ interface Variables {
8
+ userId: string;
9
+ sessionId: string;
10
+ }
11
+ /**
12
+ * Create DBSC challenge routes.
13
+ * @param options - Server options (storage, DBSC config).
14
+ * @returns A Hono sub-app with /challenge (POST) and /verify (POST) routes.
15
+ */
16
+ export declare function createDbscRoutes(options: TokenForgeServerOptions): Hono<{
17
+ Variables: Variables;
18
+ }>;
19
+ export {};
20
+ //# sourceMappingURL=dbsc-endpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dbsc-endpoint.d.ts","sourceRoot":"../src/","sources":["server/dbsc-endpoint.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAElE,UAAU,SAAS;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAuBD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAAE,SAAS,EAAE,SAAS,CAAA;CAAE,CAAC,CAwDjG"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * DBSC challenge endpoint — issues and consumes DBSC challenges.
3
+ * Mount at /api/tf/dbsc on the main Hono app alongside the binding routes.
4
+ */
5
+ import { Hono } from 'hono';
6
+ import { issueChallenge, consumeChallenge } from './dbsc-challenge.js';
7
+ import { issueBoundCookie, setBoundCookieHeader } from './bound-cookie.js';
8
+ /**
9
+ * Adapter that bridges TokenForgeStorageRef to the ChallengeStore interface
10
+ * expected by the dbsc-challenge module.
11
+ */
12
+ function makeChallengeStore(options) {
13
+ return {
14
+ async put(record) {
15
+ await options.storage.createChallenge(record);
16
+ },
17
+ async takeIfFresh(hash, now) {
18
+ // Use storage.getChallenge with hash as the ID lookup key.
19
+ // The memory storage and D1 storage both support this pattern.
20
+ const record = await options.storage.getChallenge(hash, '');
21
+ if (!record)
22
+ return null;
23
+ const typed = record;
24
+ if (new Date(typed.expiresAt) < now)
25
+ return null;
26
+ return record;
27
+ },
28
+ };
29
+ }
30
+ /**
31
+ * Create DBSC challenge routes.
32
+ * @param options - Server options (storage, DBSC config).
33
+ * @returns A Hono sub-app with /challenge (POST) and /verify (POST) routes.
34
+ */
35
+ export function createDbscRoutes(options) {
36
+ const app = new Hono();
37
+ const store = makeChallengeStore(options);
38
+ /** POST /challenge — issue a new DBSC challenge for the client to sign. */
39
+ app.post('/challenge', async (c) => {
40
+ const userId = c.get('userId');
41
+ const sessionId = c.get('sessionId');
42
+ if (!userId || !sessionId)
43
+ return c.json({ error: 'unauthorized' }, 401);
44
+ const body = await c.req.json().catch(() => ({}));
45
+ const purpose = body.purpose ?? 'refresh';
46
+ const result = await issueChallenge(store, {
47
+ tenantId: userId,
48
+ purpose,
49
+ sessionId,
50
+ });
51
+ return c.json({
52
+ challenge: result.challenge,
53
+ expiresAt: result.record.expiresAt,
54
+ });
55
+ });
56
+ /** POST /verify — consume a signed challenge and rotate the bound cookie. */
57
+ app.post('/verify', async (c) => {
58
+ const userId = c.get('userId');
59
+ const sessionId = c.get('sessionId');
60
+ if (!userId || !sessionId)
61
+ return c.json({ error: 'unauthorized' }, 401);
62
+ const body = await c.req.json();
63
+ if (!body.challenge)
64
+ return c.json({ error: 'missing_challenge' }, 400);
65
+ const purpose = body.purpose ?? 'refresh';
66
+ const result = await consumeChallenge(store, body.challenge, {
67
+ tenantId: userId,
68
+ purpose,
69
+ sessionId,
70
+ });
71
+ if (!result.ok)
72
+ return c.json({ error: result.reason }, 400);
73
+ // On successful verification, rotate the bound cookie
74
+ const deviceId = c.req.header('X-TF-Device-ID');
75
+ if (deviceId && options.dbsc?.enabled) {
76
+ const rotationInterval = options.dbsc.rotationInterval ?? 300;
77
+ const cookie = await issueBoundCookie({ maxAgeSeconds: rotationInterval });
78
+ await options.storage.updateBoundCookieHash(deviceId, cookie.hash, cookie.expiresAt);
79
+ c.header('Set-Cookie', setBoundCookieHeader(cookie));
80
+ }
81
+ return c.json({ verified: true });
82
+ });
83
+ return app;
84
+ }
85
+ //# sourceMappingURL=dbsc-endpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dbsc-endpoint.js","sourceRoot":"../src/","sources":["server/dbsc-endpoint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * DBSC (Device Bound Session Credentials) verification for the
3
+ * verifyRequest pipeline. Additive check — never blocks when ECDSA
4
+ * already passed, but sets degraded status and rotation flags.
5
+ */
6
+ import type { DbscConfig, DeviceSession } from '../shared/types.js';
7
+ /** Result of the DBSC cookie verification step. */
8
+ export interface DbscCheckResult {
9
+ /** Whether the bound cookie is valid. */
10
+ cookieValid: boolean;
11
+ /** Reason for degradation (if any). */
12
+ reason?: 'cookie_missing' | 'cookie_invalid';
13
+ /** True if the cookie should be rotated (near expiry). */
14
+ rotateCookie: boolean;
15
+ }
16
+ /**
17
+ * Parse a specific cookie value from a raw Cookie header string.
18
+ * @param cookieHeader - Raw `Cookie` header value (e.g. "a=1; b=2").
19
+ * @param name - Cookie name to look up.
20
+ * @returns The cookie value, or null if not found.
21
+ */
22
+ export declare function parseCookie(cookieHeader: string | null, name: string): string | null;
23
+ /**
24
+ * Run the DBSC bound-cookie check against a device session.
25
+ *
26
+ * @param cookieHeader - Raw Cookie header from the request.
27
+ * @param session - The device session (must include bound_cookie_hash).
28
+ * @param dbsc - DBSC configuration.
29
+ * @returns Check result with validity, reason, and rotation flag.
30
+ */
31
+ export declare function verifyDbscCookie(cookieHeader: string | null, session: DeviceSession, dbsc: DbscConfig): Promise<DbscCheckResult>;
32
+ //# sourceMappingURL=dbsc-verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dbsc-verify.d.ts","sourceRoot":"../src/","sources":["server/dbsc-verify.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAKpE,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,yCAAyC;IACzC,WAAW,EAAE,OAAO,CAAC;IACrB,uCAAuC;IACvC,MAAM,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,CAAC;IAC7C,0DAA0D;IAC1D,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUpF;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,eAAe,CAAC,CA0B1B"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * DBSC (Device Bound Session Credentials) verification for the
3
+ * verifyRequest pipeline. Additive check — never blocks when ECDSA
4
+ * already passed, but sets degraded status and rotation flags.
5
+ */
6
+ import { hashBoundCookie, BOUND_COOKIE_NAME } from './bound-cookie.js';
7
+ const DEFAULT_COOKIE_NAME = BOUND_COOKIE_NAME;
8
+ const DEFAULT_ROTATION_INTERVAL = 300;
9
+ /**
10
+ * Parse a specific cookie value from a raw Cookie header string.
11
+ * @param cookieHeader - Raw `Cookie` header value (e.g. "a=1; b=2").
12
+ * @param name - Cookie name to look up.
13
+ * @returns The cookie value, or null if not found.
14
+ */
15
+ export function parseCookie(cookieHeader, name) {
16
+ if (!cookieHeader)
17
+ return null;
18
+ const pairs = cookieHeader.split(';');
19
+ for (const pair of pairs) {
20
+ const eqIdx = pair.indexOf('=');
21
+ if (eqIdx === -1)
22
+ continue;
23
+ const key = pair.slice(0, eqIdx).trim();
24
+ if (key === name)
25
+ return pair.slice(eqIdx + 1).trim();
26
+ }
27
+ return null;
28
+ }
29
+ /**
30
+ * Run the DBSC bound-cookie check against a device session.
31
+ *
32
+ * @param cookieHeader - Raw Cookie header from the request.
33
+ * @param session - The device session (must include bound_cookie_hash).
34
+ * @param dbsc - DBSC configuration.
35
+ * @returns Check result with validity, reason, and rotation flag.
36
+ */
37
+ export async function verifyDbscCookie(cookieHeader, session, dbsc) {
38
+ const cookieName = dbsc.cookieName ?? DEFAULT_COOKIE_NAME;
39
+ const rotationInterval = dbsc.rotationInterval ?? DEFAULT_ROTATION_INTERVAL;
40
+ const cookieValue = parseCookie(cookieHeader, cookieName);
41
+ if (!cookieValue) {
42
+ return { cookieValid: false, reason: 'cookie_missing', rotateCookie: false };
43
+ }
44
+ const storedHash = session.bound_cookie_hash;
45
+ if (!storedHash) {
46
+ return { cookieValid: false, reason: 'cookie_missing', rotateCookie: false };
47
+ }
48
+ const computedHash = await hashBoundCookie(cookieValue);
49
+ if (computedHash !== storedHash) {
50
+ return { cookieValid: false, reason: 'cookie_invalid', rotateCookie: false };
51
+ }
52
+ // Check if cookie is near expiry (< half rotation interval remaining)
53
+ const rotateCookie = isCookieNearExpiry(session.bound_cookie_expires_at, rotationInterval);
54
+ return { cookieValid: true, rotateCookie };
55
+ }
56
+ /** True when the cookie has less than half its rotation interval remaining. */
57
+ function isCookieNearExpiry(expiresAt, rotationInterval) {
58
+ if (!expiresAt)
59
+ return true;
60
+ const expiresMs = new Date(expiresAt).getTime();
61
+ const now = Date.now();
62
+ const remainingSeconds = (expiresMs - now) / 1000;
63
+ return remainingSeconds < rotationInterval / 2;
64
+ }
65
+ //# sourceMappingURL=dbsc-verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dbsc-verify.js","sourceRoot":"../src/","sources":["server/dbsc-verify.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGvE,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;AAC9C,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAYtC;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,YAA2B,EAAE,IAAY;IACnE,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC;IAC/B,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,KAAK,KAAK,CAAC,CAAC;YAAE,SAAS;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACxD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,YAA2B,EAC3B,OAAsB,EACtB,IAAgB;IAEhB,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,mBAAmB,CAAC;IAC1D,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,IAAI,yBAAyB,CAAC;IAE5E,MAAM,WAAW,GAAG,WAAW,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAC1D,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IAC/E,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAC7C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IAC/E,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC,WAAW,CAAC,CAAC;IACxD,IAAI,YAAY,KAAK,UAAU,EAAE,CAAC;QAChC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IAC/E,CAAC;IAED,sEAAsE;IACtE,MAAM,YAAY,GAAG,kBAAkB,CACrC,OAAO,CAAC,uBAAuB,EAC/B,gBAAgB,CACjB,CAAC;IAEF,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;AAC7C,CAAC;AAED,+EAA+E;AAC/E,SAAS,kBAAkB,CACzB,SAAoC,EACpC,gBAAwB;IAExB,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;IAChD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,gBAAgB,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC;IAClD,OAAO,gBAAgB,GAAG,gBAAgB,GAAG,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type TokenForgeIdentity, type TokenForgeIdentityProfileField } from '../shared/identity.js';
2
+ export interface ClearTokenForgeIdentityProfileOptions {
3
+ apiKey: string;
4
+ identity: TokenForgeIdentity;
5
+ apiBase?: string;
6
+ fetch?: typeof fetch;
7
+ timeoutMs?: number;
8
+ }
9
+ export interface ClearTokenForgeIdentityProfileResult {
10
+ externalUserRef: string;
11
+ cleared: TokenForgeIdentityProfileField[];
12
+ }
13
+ export declare class TokenForgeIdentityProfileRejectedError extends Error {
14
+ }
15
+ export declare class TokenForgeIdentityProfileUnavailableError extends Error {
16
+ }
17
+ export declare function clearTokenForgeIdentityProfile(options: ClearTokenForgeIdentityProfileOptions): Promise<ClearTokenForgeIdentityProfileResult>;
18
+ //# sourceMappingURL=identity-profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-profile.d.ts","sourceRoot":"../src/","sources":["server/identity-profile.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,8BAA8B,EACpC,MAAM,uBAAuB,CAAC;AAK/B,MAAM,WAAW,qCAAqC;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oCAAoC;IACnD,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,8BAA8B,EAAE,CAAC;CAC3C;AAED,qBAAa,sCAAuC,SAAQ,KAAK;CAAG;AACpE,qBAAa,yCAA0C,SAAQ,KAAK;CAAG;AAEvE,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,qCAAqC,GAC7C,OAAO,CAAC,oCAAoC,CAAC,CA4E/C"}
@@ -0,0 +1,113 @@
1
+ import { validateTokenForgeIdentity, } from '../shared/identity.js';
2
+ const DEFAULT_API_BASE = 'https://tokenforge-api.opensyber.cloud';
3
+ const MAX_RESPONSE_BYTES = 64 * 1024;
4
+ export class TokenForgeIdentityProfileRejectedError extends Error {
5
+ }
6
+ export class TokenForgeIdentityProfileUnavailableError extends Error {
7
+ }
8
+ export async function clearTokenForgeIdentityProfile(options) {
9
+ if (!/^tf_[0-9a-f]{32}$/.test(options.apiKey)) {
10
+ throw new TokenForgeIdentityProfileUnavailableError('TokenForge API key is invalid');
11
+ }
12
+ let identity;
13
+ try {
14
+ identity = validateTokenForgeIdentity(options.identity);
15
+ }
16
+ catch {
17
+ throw new TokenForgeIdentityProfileRejectedError('TokenForge profile clear request is invalid');
18
+ }
19
+ if (identity.profile !== undefined ||
20
+ !identity.clearProfile?.length ||
21
+ identity.idToken !== undefined ||
22
+ identity.workforceAppId !== undefined) {
23
+ throw new TokenForgeIdentityProfileRejectedError('TokenForge profile clear request is invalid');
24
+ }
25
+ const base = new URL(options.apiBase ?? DEFAULT_API_BASE);
26
+ if (base.protocol !== 'https:' || base.username || base.password || base.search || base.hash) {
27
+ throw new TokenForgeIdentityProfileUnavailableError('TokenForge API URL is invalid');
28
+ }
29
+ const request = options.fetch ?? globalThis.fetch;
30
+ const timeoutMs = Number.isSafeInteger(options.timeoutMs) && (options.timeoutMs ?? 0) > 0
31
+ ? options.timeoutMs
32
+ : 1500;
33
+ const controller = new AbortController();
34
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
35
+ try {
36
+ const response = await request(new URL('/v2/identity/profile', base), {
37
+ method: 'POST',
38
+ headers: {
39
+ Authorization: `Bearer ${options.apiKey}`,
40
+ Accept: 'application/json',
41
+ 'Content-Type': 'application/json',
42
+ },
43
+ body: JSON.stringify({ identity }),
44
+ signal: controller.signal,
45
+ });
46
+ const raw = await readBoundedResponse(response, MAX_RESPONSE_BYTES);
47
+ if (!response.ok) {
48
+ if (response.status >= 400 && response.status < 500 && response.status !== 429) {
49
+ throw new TokenForgeIdentityProfileRejectedError('TokenForge profile clear was rejected');
50
+ }
51
+ throw new TokenForgeIdentityProfileUnavailableError('TokenForge profile clear is unavailable');
52
+ }
53
+ let parsed;
54
+ try {
55
+ parsed = JSON.parse(raw);
56
+ }
57
+ catch {
58
+ throw new TokenForgeIdentityProfileUnavailableError('TokenForge profile clear response is invalid');
59
+ }
60
+ const data = parsed.data;
61
+ if (!data ||
62
+ data.externalUserRef !== identity.externalUserRef ||
63
+ !Array.isArray(data.cleared) ||
64
+ data.cleared.length !== identity.clearProfile.length ||
65
+ !data.cleared.every((field, index) => field === identity.clearProfile?.[index])) {
66
+ throw new TokenForgeIdentityProfileUnavailableError('TokenForge profile clear response is invalid');
67
+ }
68
+ return data;
69
+ }
70
+ catch (error) {
71
+ if (error instanceof TokenForgeIdentityProfileRejectedError ||
72
+ error instanceof TokenForgeIdentityProfileUnavailableError) {
73
+ throw error;
74
+ }
75
+ throw new TokenForgeIdentityProfileUnavailableError('TokenForge profile clear is unavailable');
76
+ }
77
+ finally {
78
+ clearTimeout(timeout);
79
+ }
80
+ }
81
+ async function readBoundedResponse(response, limit) {
82
+ if (!response.body)
83
+ return '';
84
+ const reader = response.body.getReader();
85
+ const chunks = [];
86
+ let size = 0;
87
+ try {
88
+ for (;;) {
89
+ const { done, value } = await reader.read();
90
+ if (done)
91
+ break;
92
+ size += value.byteLength;
93
+ if (size > limit) {
94
+ await reader.cancel();
95
+ throw new TokenForgeIdentityProfileUnavailableError('TokenForge profile clear response is invalid');
96
+ }
97
+ chunks.push(value);
98
+ }
99
+ }
100
+ catch (error) {
101
+ if (error instanceof TokenForgeIdentityProfileUnavailableError)
102
+ throw error;
103
+ throw new TokenForgeIdentityProfileUnavailableError('TokenForge profile clear response is invalid');
104
+ }
105
+ const joined = new Uint8Array(size);
106
+ let offset = 0;
107
+ for (const chunk of chunks) {
108
+ joined.set(chunk, offset);
109
+ offset += chunk.byteLength;
110
+ }
111
+ return new TextDecoder().decode(joined);
112
+ }
113
+ //# sourceMappingURL=identity-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-profile.js","sourceRoot":"../src/","sources":["server/identity-profile.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,15 @@
1
1
  export { tokenForgeMiddleware } from './middleware.js';
2
2
  export type { TokenForgeOptions } from './middleware.js';
3
+ export { verifyWebAuthnAttestation, verifyWebAuthnAssertion, verifyClientDataJSON, } from './webauthn-verify.js';
4
+ export type { AttestationVerifyResult } from './webauthn-verify.js';
5
+ export { bindDeviceWithIdToken } from './bind-with-id-token.js';
6
+ export type { BindWithIdTokenInput, BindWithIdTokenResult, } from './bind-with-id-token.js';
7
+ export { createTokenForgeCloudBindHandler } from './cloud-bind.js';
8
+ export type { CloudBindHandlerOptions, CloudBinding, CloudBindingStore, CloudIdentityResolver, CloudSession, CloudSessionResolver, TokenForgeCloudMode, } from './cloud-bind.js';
9
+ export { readTokenForgeProof, verifyTokenForgeCloudRequest, } from './cloud-verify.js';
10
+ export { clearTokenForgeIdentityProfile, TokenForgeIdentityProfileRejectedError, TokenForgeIdentityProfileUnavailableError, } from './identity-profile.js';
11
+ export type { ClearTokenForgeIdentityProfileOptions, ClearTokenForgeIdentityProfileResult, } from './identity-profile.js';
12
+ export { createTokenForgeCloudMiddleware } from './cloud-middleware.js';
13
+ export type { CloudCircuitState, CloudTelemetryEvent, CloudTelemetryHook, CloudVerifyDecision, TokenForgeCloudMiddlewareOptions, TokenForgeRequestHandler, } from './cloud-middleware.js';
14
+ export type { CloudReplayStore, CloudVerificationBindingStore, CloudVerifyOptions, TokenForgeLocalVerification, TokenForgeProofWithSignature, } from './cloud-verify.js';
3
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"../src/","sources":["server/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAEpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EACV,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,gCAAgC,EAAE,MAAM,iBAAiB,CAAC;AACnE,YAAY,EACV,uBAAuB,EACvB,YAAY,EACZ,iBAAiB,EACjB,qBAAqB,EACrB,YAAY,EACZ,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,8BAA8B,EAC9B,sCAAsC,EACtC,yCAAyC,GAC1C,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,qCAAqC,EACrC,oCAAoC,GACrC,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,+BAA+B,EAAE,MAAM,uBAAuB,CAAC;AACxE,YAAY,EACV,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,gCAAgC,EAChC,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,gBAAgB,EAChB,6BAA6B,EAC7B,kBAAkB,EAClB,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,mBAAmB,CAAC"}
@@ -3,4 +3,11 @@
3
3
  // Self-hosted routes (binding, step-up) and internal crypto utilities (trust-score,
4
4
  // verify, crypto) are kept internal and excluded via package.json `files`.
5
5
  export { tokenForgeMiddleware } from './middleware.js';
6
+ // FIDO2/WebAuthn server-side verification (Sprint E1).
7
+ export { verifyWebAuthnAttestation, verifyWebAuthnAssertion, verifyClientDataJSON, } from './webauthn-verify.js';
8
+ export { bindDeviceWithIdToken } from './bind-with-id-token.js';
9
+ export { createTokenForgeCloudBindHandler } from './cloud-bind.js';
10
+ export { readTokenForgeProof, verifyTokenForgeCloudRequest, } from './cloud-verify.js';
11
+ export { clearTokenForgeIdentityProfile, TokenForgeIdentityProfileRejectedError, TokenForgeIdentityProfileUnavailableError, } from './identity-profile.js';
12
+ export { createTokenForgeCloudMiddleware } from './cloud-middleware.js';
6
13
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,+EAA+E;AAC/E,oFAAoF;AACpF,2EAA2E;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"../src/","sources":["server/index.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,+EAA+E;AAC/E,oFAAoF;AACpF,2EAA2E;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD,uDAAuD;AACvD,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAMhE,OAAO,EAAE,gCAAgC,EAAE,MAAM,iBAAiB,CAAC;AAWnE,OAAO,EACL,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,8BAA8B,EAC9B,sCAAsC,EACtC,yCAAyC,GAC1C,MAAM,uBAAuB,CAAC;AAM/B,OAAO,EAAE,+BAA+B,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Compact JWS verifier for DBSC refresh + action signing.
3
+ *
4
+ * Accepts compact-form JWS strings: `<header>.<payload>.<signature>` —
5
+ * all three parts base64url-encoded. Validates the protected header
6
+ * (alg, typ), the payload claims (iat, exp, nonce, sub), and the
7
+ * signature against the bound public key.
8
+ *
9
+ * Public key may be JWK JSON or PEM-SPKI; the existing
10
+ * `importPublicKey` in ./crypto.ts handles both, so this file stays
11
+ * focused on JWS framing.
12
+ */
13
+ export interface JwsClaims {
14
+ /** Subject — DBSC sessionId or device id. */
15
+ sub: string;
16
+ /** Issued-at, epoch seconds. */
17
+ iat: number;
18
+ /** Expiry, epoch seconds. */
19
+ exp: number;
20
+ /** Per-message nonce — checked against challenge store. */
21
+ nonce: string;
22
+ /** Optional action descriptor (per-action step-up). */
23
+ action?: string;
24
+ /** Optional SHA-256 over the action body, base64url. */
25
+ actionHash?: string;
26
+ /** Optional TLS exporter binding, hex. */
27
+ tlsExporter?: string;
28
+ }
29
+ export interface VerifyJwsOptions {
30
+ /** Bound public key string (JWK or PEM SPKI). */
31
+ publicKey: string;
32
+ /** Maximum age (seconds) of the JWS — defaults to 60. */
33
+ maxAgeSeconds?: number;
34
+ /** Override clock for tests. */
35
+ now?: Date;
36
+ }
37
+ export type VerifyJwsResult = {
38
+ ok: true;
39
+ claims: JwsClaims;
40
+ protectedHeader: Record<string, unknown>;
41
+ } | {
42
+ ok: false;
43
+ reason: string;
44
+ };
45
+ export declare function verifyCompactJws(jws: string, opts: VerifyJwsOptions): Promise<VerifyJwsResult>;
46
+ //# sourceMappingURL=jws-verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jws-verify.d.ts","sourceRoot":"../src/","sources":["server/jws-verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,WAAW,SAAS;IACxB,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,6BAA6B;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACzE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAIlC,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,eAAe,CAAC,CA0C1B"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Compact JWS verifier for DBSC refresh + action signing.
3
+ *
4
+ * Accepts compact-form JWS strings: `<header>.<payload>.<signature>` —
5
+ * all three parts base64url-encoded. Validates the protected header
6
+ * (alg, typ), the payload claims (iat, exp, nonce, sub), and the
7
+ * signature against the bound public key.
8
+ *
9
+ * Public key may be JWK JSON or PEM-SPKI; the existing
10
+ * `importPublicKey` in ./crypto.ts handles both, so this file stays
11
+ * focused on JWS framing.
12
+ */
13
+ import { importPublicKey, verifySignature } from './crypto.js';
14
+ const SUPPORTED_ALGS = new Set(['ES256']);
15
+ export async function verifyCompactJws(jws, opts) {
16
+ const parts = jws.split('.');
17
+ if (parts.length !== 3)
18
+ return { ok: false, reason: 'jws_malformed' };
19
+ const [headerB64, payloadB64, sigB64] = parts;
20
+ let header;
21
+ let payload;
22
+ try {
23
+ header = JSON.parse(b64UrlDecodeString(headerB64));
24
+ payload = JSON.parse(b64UrlDecodeString(payloadB64));
25
+ }
26
+ catch {
27
+ return { ok: false, reason: 'jws_malformed' };
28
+ }
29
+ const alg = header.alg;
30
+ if (typeof alg !== 'string' || !SUPPORTED_ALGS.has(alg)) {
31
+ return { ok: false, reason: 'jws_unsupported_alg' };
32
+ }
33
+ const claims = payload;
34
+ if (!claims.sub || !claims.nonce || typeof claims.iat !== 'number' || typeof claims.exp !== 'number') {
35
+ return { ok: false, reason: 'jws_missing_claims' };
36
+ }
37
+ const now = opts.now ?? new Date();
38
+ const nowSec = Math.floor(now.getTime() / 1000);
39
+ const maxAge = opts.maxAgeSeconds ?? 60;
40
+ if (claims.exp < nowSec)
41
+ return { ok: false, reason: 'jws_expired' };
42
+ if (claims.iat > nowSec + 5)
43
+ return { ok: false, reason: 'jws_future_iat' };
44
+ if (nowSec - claims.iat > maxAge)
45
+ return { ok: false, reason: 'jws_too_old' };
46
+ const signingInput = `${headerB64}.${payloadB64}`;
47
+ let key;
48
+ try {
49
+ key = await importPublicKey(opts.publicKey);
50
+ }
51
+ catch {
52
+ return { ok: false, reason: 'jws_bad_public_key' };
53
+ }
54
+ const ok = await verifySignature(key, sigB64, signingInput);
55
+ if (!ok)
56
+ return { ok: false, reason: 'jws_bad_signature' };
57
+ return { ok: true, claims: claims, protectedHeader: header };
58
+ }
59
+ function b64UrlDecodeString(b64) {
60
+ const s = b64.replace(/-/g, '+').replace(/_/g, '/');
61
+ const padded = s + '='.repeat((4 - (s.length % 4)) % 4);
62
+ return atob(padded);
63
+ }
64
+ //# sourceMappingURL=jws-verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jws-verify.js","sourceRoot":"../src/","sources":["server/jws-verify.ts"],"names":[],"mappings":"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"}