@opensyber/tokenforge 1.0.0 → 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 (162) hide show
  1. package/README.md +130 -11
  2. package/dist/adapters/astro.d.ts +9 -0
  3. package/dist/adapters/astro.d.ts.map +1 -1
  4. package/dist/adapters/astro.js +13 -0
  5. package/dist/adapters/astro.js.map +1 -1
  6. package/dist/adapters/cloud-v2-bridge.d.ts +8 -0
  7. package/dist/adapters/cloud-v2-bridge.d.ts.map +1 -0
  8. package/dist/adapters/cloud-v2-bridge.js +24 -0
  9. package/dist/adapters/cloud-v2-bridge.js.map +1 -0
  10. package/dist/adapters/express.d.ts +15 -2
  11. package/dist/adapters/express.d.ts.map +1 -1
  12. package/dist/adapters/express.js +38 -0
  13. package/dist/adapters/express.js.map +1 -1
  14. package/dist/adapters/fastify.d.ts +15 -6
  15. package/dist/adapters/fastify.d.ts.map +1 -1
  16. package/dist/adapters/fastify.js +38 -0
  17. package/dist/adapters/fastify.js.map +1 -1
  18. package/dist/adapters/hono.d.ts +12 -0
  19. package/dist/adapters/hono.d.ts.map +1 -1
  20. package/dist/adapters/hono.js +13 -0
  21. package/dist/adapters/hono.js.map +1 -1
  22. package/dist/adapters/index.d.ts.map +1 -1
  23. package/dist/adapters/index.js.map +1 -1
  24. package/dist/adapters/nextjs.d.ts +15 -0
  25. package/dist/adapters/nextjs.d.ts.map +1 -1
  26. package/dist/adapters/nextjs.js +57 -30
  27. package/dist/adapters/nextjs.js.map +1 -1
  28. package/dist/adapters/sveltekit.d.ts +9 -0
  29. package/dist/adapters/sveltekit.d.ts.map +1 -1
  30. package/dist/adapters/sveltekit.js +13 -0
  31. package/dist/adapters/sveltekit.js.map +1 -1
  32. package/dist/client/action-signer.d.ts.map +1 -1
  33. package/dist/client/action-signer.js.map +1 -1
  34. package/dist/client/binding.d.ts.map +1 -1
  35. package/dist/client/binding.js.map +1 -1
  36. package/dist/client/cloud-device.d.ts +54 -0
  37. package/dist/client/cloud-device.d.ts.map +1 -0
  38. package/dist/client/cloud-device.js +301 -0
  39. package/dist/client/cloud-device.js.map +1 -0
  40. package/dist/client/crypto.d.ts.map +1 -1
  41. package/dist/client/crypto.js.map +1 -1
  42. package/dist/client/index.d.ts +1 -0
  43. package/dist/client/index.d.ts.map +1 -1
  44. package/dist/client/index.js +3 -1
  45. package/dist/client/index.js.map +1 -1
  46. package/dist/client/interceptor.d.ts.map +1 -1
  47. package/dist/client/interceptor.js +27 -3
  48. package/dist/client/interceptor.js.map +1 -1
  49. package/dist/client/signer.d.ts +23 -3
  50. package/dist/client/signer.d.ts.map +1 -1
  51. package/dist/client/signer.js +33 -5
  52. package/dist/client/signer.js.map +1 -1
  53. package/dist/client/storage.d.ts.map +1 -1
  54. package/dist/client/storage.js.map +1 -1
  55. package/dist/client/webauthn.d.ts.map +1 -1
  56. package/dist/client/webauthn.js.map +1 -1
  57. package/dist/protocol/v2.d.ts +23 -0
  58. package/dist/protocol/v2.d.ts.map +1 -0
  59. package/dist/protocol/v2.js +157 -0
  60. package/dist/protocol/v2.js.map +1 -0
  61. package/dist/react/index.d.ts.map +1 -1
  62. package/dist/react/index.js.map +1 -1
  63. package/dist/react/provider.d.ts.map +1 -1
  64. package/dist/react/provider.js.map +1 -1
  65. package/dist/server/action-verify.d.ts.map +1 -1
  66. package/dist/server/action-verify.js +1 -1
  67. package/dist/server/action-verify.js.map +1 -1
  68. package/dist/server/activity-histogram.d.ts +47 -0
  69. package/dist/server/activity-histogram.d.ts.map +1 -0
  70. package/dist/server/activity-histogram.js +76 -0
  71. package/dist/server/activity-histogram.js.map +1 -0
  72. package/dist/server/aitm-heuristics.d.ts.map +1 -1
  73. package/dist/server/aitm-heuristics.js.map +1 -1
  74. package/dist/server/bind-with-id-token.d.ts +55 -0
  75. package/dist/server/bind-with-id-token.d.ts.map +1 -0
  76. package/dist/server/bind-with-id-token.js +70 -0
  77. package/dist/server/bind-with-id-token.js.map +1 -0
  78. package/dist/server/bound-cookie.d.ts.map +1 -1
  79. package/dist/server/bound-cookie.js.map +1 -1
  80. package/dist/server/cbor.d.ts.map +1 -1
  81. package/dist/server/cbor.js.map +1 -1
  82. package/dist/server/cloud-bind.d.ts +42 -0
  83. package/dist/server/cloud-bind.d.ts.map +1 -0
  84. package/dist/server/cloud-bind.js +387 -0
  85. package/dist/server/cloud-bind.js.map +1 -0
  86. package/dist/server/cloud-middleware.d.ts +46 -0
  87. package/dist/server/cloud-middleware.d.ts.map +1 -0
  88. package/dist/server/cloud-middleware.js +344 -0
  89. package/dist/server/cloud-middleware.js.map +1 -0
  90. package/dist/server/cloud-verify.d.ts +30 -0
  91. package/dist/server/cloud-verify.d.ts.map +1 -0
  92. package/dist/server/cloud-verify.js +206 -0
  93. package/dist/server/cloud-verify.js.map +1 -0
  94. package/dist/server/dbsc-challenge.d.ts.map +1 -1
  95. package/dist/server/dbsc-challenge.js.map +1 -1
  96. package/dist/server/dbsc-endpoint.d.ts +20 -0
  97. package/dist/server/dbsc-endpoint.d.ts.map +1 -0
  98. package/dist/server/dbsc-endpoint.js +85 -0
  99. package/dist/server/dbsc-endpoint.js.map +1 -0
  100. package/dist/server/dbsc-verify.d.ts +32 -0
  101. package/dist/server/dbsc-verify.d.ts.map +1 -0
  102. package/dist/server/dbsc-verify.js +65 -0
  103. package/dist/server/dbsc-verify.js.map +1 -0
  104. package/dist/server/identity-profile.d.ts +18 -0
  105. package/dist/server/identity-profile.d.ts.map +1 -0
  106. package/dist/server/identity-profile.js +113 -0
  107. package/dist/server/identity-profile.js.map +1 -0
  108. package/dist/server/index.d.ts +10 -0
  109. package/dist/server/index.d.ts.map +1 -1
  110. package/dist/server/index.js +5 -0
  111. package/dist/server/index.js.map +1 -1
  112. package/dist/server/jws-verify.d.ts.map +1 -1
  113. package/dist/server/jws-verify.js.map +1 -1
  114. package/dist/server/middleware.d.ts +9 -0
  115. package/dist/server/middleware.d.ts.map +1 -1
  116. package/dist/server/middleware.js +60 -20
  117. package/dist/server/middleware.js.map +1 -1
  118. package/dist/server/oidc-verify.d.ts +16 -1
  119. package/dist/server/oidc-verify.d.ts.map +1 -1
  120. package/dist/server/oidc-verify.js +9 -1
  121. package/dist/server/oidc-verify.js.map +1 -1
  122. package/dist/server/policy.d.ts.map +1 -1
  123. package/dist/server/policy.js.map +1 -1
  124. package/dist/server/step-up-policy.d.ts.map +1 -1
  125. package/dist/server/step-up-policy.js.map +1 -1
  126. package/dist/server/storage/index.d.ts +3 -0
  127. package/dist/server/storage/index.d.ts.map +1 -0
  128. package/dist/server/storage/index.js +2 -0
  129. package/dist/server/storage/index.js.map +1 -0
  130. package/dist/server/storage/interface.d.ts +73 -0
  131. package/dist/server/storage/interface.d.ts.map +1 -0
  132. package/dist/server/storage/interface.js +2 -0
  133. package/dist/server/storage/interface.js.map +1 -0
  134. package/dist/server/storage/memory.d.ts +50 -0
  135. package/dist/server/storage/memory.d.ts.map +1 -0
  136. package/dist/server/storage/memory.js +121 -0
  137. package/dist/server/storage/memory.js.map +1 -0
  138. package/dist/server/webauthn-verify.d.ts.map +1 -1
  139. package/dist/server/webauthn-verify.js.map +1 -1
  140. package/dist/server/webhooks.d.ts.map +1 -1
  141. package/dist/server/webhooks.js.map +1 -1
  142. package/dist/shared/action-hash.d.ts.map +1 -1
  143. package/dist/shared/action-hash.js.map +1 -1
  144. package/dist/shared/identity.d.ts +45 -0
  145. package/dist/shared/identity.d.ts.map +1 -0
  146. package/dist/shared/identity.js +176 -0
  147. package/dist/shared/identity.js.map +1 -0
  148. package/dist/shared/types-internal.d.ts.map +1 -1
  149. package/dist/shared/types-internal.js.map +1 -1
  150. package/dist/shared/types.d.ts +47 -2
  151. package/dist/shared/types.d.ts.map +1 -1
  152. package/dist/shared/types.js +1 -0
  153. package/dist/shared/types.js.map +1 -1
  154. package/package.json +9 -47
  155. package/dist/server/trust-score-fixtures.d.ts +0 -9
  156. package/dist/server/trust-score-fixtures.d.ts.map +0 -1
  157. package/dist/server/trust-score-fixtures.js +0 -23
  158. package/dist/server/trust-score-fixtures.js.map +0 -1
  159. package/dist/server/webauthn-verify.test-helpers.d.ts +0 -8
  160. package/dist/server/webauthn-verify.test-helpers.d.ts.map +0 -1
  161. package/dist/server/webauthn-verify.test-helpers.js +0 -39
  162. package/dist/server/webauthn-verify.test-helpers.js.map +0 -1
@@ -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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"}
@@ -2,4 +2,14 @@ export { tokenForgeMiddleware } from './middleware.js';
2
2
  export type { TokenForgeOptions } from './middleware.js';
3
3
  export { verifyWebAuthnAttestation, verifyWebAuthnAssertion, verifyClientDataJSON, } from './webauthn-verify.js';
4
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';
5
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;AAEzD,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,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"}
@@ -5,4 +5,9 @@
5
5
  export { tokenForgeMiddleware } from './middleware.js';
6
6
  // FIDO2/WebAuthn server-side verification (Sprint E1).
7
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';
8
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;AAEvD,uDAAuD;AACvD,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,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"}
@@ -1 +1 @@
1
- {"version":3,"file":"jws-verify.d.ts","sourceRoot":"","sources":["../../src/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"}
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"}
@@ -1 +1 @@
1
- {"version":3,"file":"jws-verify.js","sourceRoot":"","sources":["../../src/server/jws-verify.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jws-verify.js","sourceRoot":"../src/","sources":["server/jws-verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAgC/D,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AAE1C,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAW,EACX,IAAsB;IAEtB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IACtE,MAAM,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,GAAG,KAAiC,CAAC;IAE1E,IAAI,MAA+B,CAAC;IACpC,IAAI,OAAgC,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC;QACnD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAChD,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC;IACvB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QACxD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;IACtD,CAAC;IAED,MAAM,MAAM,GAAG,OAA6B,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;QACrG,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;IACrD,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACnC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC;IACxC,IAAI,MAAM,CAAC,GAAG,GAAG,MAAM;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IACrE,IAAI,MAAM,CAAC,GAAG,GAAG,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC5E,IAAI,MAAM,GAAG,MAAM,CAAC,GAAG,GAAG,MAAM;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAE9E,MAAM,YAAY,GAAG,GAAG,SAAS,IAAI,UAAU,EAAE,CAAC;IAClD,IAAI,GAAG,CAAC;IACR,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;IACrD,CAAC;IACD,MAAM,EAAE,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;IAC5D,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;IAE3D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAmB,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC;AAC5E,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAW;IACrC,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC"}
@@ -13,6 +13,15 @@ export interface TokenForgeOptions {
13
13
  allowThreshold?: number;
14
14
  /** Trust score threshold for step-up (default: 40). */
15
15
  stepUpThreshold?: number;
16
+ /**
17
+ * Behavior when the request cannot be positively verified — missing TF
18
+ * headers on a protected path, or an unreachable/erroring TokenForge API.
19
+ *
20
+ * - `'closed'` (default): default-deny. Reject with 401 (missing binding)
21
+ * or 503 (backend unavailable). Bulletproof posture.
22
+ * - `'open'`: legacy fail-open — degrade through. Staged rollout only.
23
+ */
24
+ failMode?: 'closed' | 'open';
16
25
  }
17
26
  /**
18
27
  * TokenForge Hono middleware — verifies device-bound sessions via the
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAI7D,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAcD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,iBAAiB,GACzB,iBAAiB,CAgFnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,IAAI,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,iBAAiB,CAcxF;AAED,gBAAgB;AAChB,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAMtE;AAED,gBAAgB;AAChB,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,EAAE,GACtB,OAAO,CAaT"}
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"../src/","sources":["server/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAI7D,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CAC9B;AAcD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,iBAAiB,GACzB,iBAAiB,CAwGnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,IAAI,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,iBAAiB,CAcxF;AAsBD,gBAAgB;AAChB,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAMtE;AAED,gBAAgB;AAChB,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,EAAE,GACtB,OAAO,CAaT"}
@@ -11,6 +11,7 @@ export function tokenForgeMiddleware(options) {
11
11
  const apiBase = options.apiBase ?? DEFAULT_API_BASE;
12
12
  const allowThreshold = options.allowThreshold ?? 80;
13
13
  const stepUpThreshold = options.stepUpThreshold ?? 40;
14
+ const failMode = options.failMode ?? 'closed';
14
15
  return async (c, next) => {
15
16
  if (shouldSkip(c.req.path, options.skipPaths))
16
17
  return next();
@@ -20,30 +21,52 @@ export function tokenForgeMiddleware(options) {
20
21
  timestamp: c.req.header('X-TF-Timestamp') ?? null,
21
22
  deviceId: c.req.header('X-TF-Device-ID') ?? null,
22
23
  };
24
+ // Default-deny: a protected path (not in skipPaths) with no TF headers
25
+ // cannot be verified. Reject locally before spending an API round-trip.
26
+ if (!headers.signature || !headers.nonce || !headers.timestamp || !headers.deviceId) {
27
+ if (failMode === 'open') {
28
+ c.set('tf', { bound: false, trustScore: 0, deviceId: null });
29
+ return next();
30
+ }
31
+ return c.json({
32
+ error: 'device_binding_required',
33
+ action: 'bind',
34
+ message: 'This operation requires device verification',
35
+ }, 401);
36
+ }
23
37
  // Call TokenForge API for verification
24
- const res = await fetch(`${apiBase}/v1/edge/verify`, {
25
- method: 'POST',
26
- headers: {
27
- Authorization: `Bearer ${options.apiKey}`,
28
- 'Content-Type': 'application/json',
29
- },
30
- body: JSON.stringify({
31
- path: c.req.path,
32
- method: c.req.method,
33
- headers,
34
- ipAddress: c.req.header('cf-connecting-ip')
35
- ?? c.req.header('x-forwarded-for') ?? '',
36
- countryCode: c.req.header('cf-ipcountry') ?? '',
37
- userAgent: c.req.header('user-agent') ?? '',
38
- }),
39
- });
38
+ let res;
39
+ try {
40
+ res = await fetch(`${apiBase}/v1/edge/verify`, {
41
+ method: 'POST',
42
+ headers: {
43
+ Authorization: `Bearer ${options.apiKey}`,
44
+ 'Content-Type': 'application/json',
45
+ },
46
+ body: JSON.stringify({
47
+ path: c.req.path,
48
+ method: c.req.method,
49
+ headers,
50
+ ipAddress: c.req.header('cf-connecting-ip')
51
+ ?? c.req.header('x-forwarded-for') ?? '',
52
+ countryCode: c.req.header('cf-ipcountry') ?? '',
53
+ userAgent: c.req.header('user-agent') ?? '',
54
+ }),
55
+ });
56
+ }
57
+ catch (err) {
58
+ // Network failure reaching the verification backend.
59
+ return handleUnavailable(c, next, failMode, `fetch: ${String(err)}`);
60
+ }
40
61
  if (!res.ok) {
41
- // API unreachable degrade gracefully
42
- console.error('[TokenForge] API error:', res.status);
43
- c.set('tf', { bound: false, trustScore: 0, deviceId: null });
44
- return next();
62
+ return handleUnavailable(c, next, failMode, `status ${res.status}`);
45
63
  }
46
64
  const { data } = (await res.json());
65
+ // Backend could not complete verification. Under fail-closed this must
66
+ // NOT pass — an unverifiable session is treated as unavailable.
67
+ if (data.status === 'degraded') {
68
+ return handleUnavailable(c, next, failMode, 'edge_degraded');
69
+ }
47
70
  if (data.status === 'block') {
48
71
  return c.json({
49
72
  error: 'session_blocked',
@@ -104,6 +127,23 @@ export function requireFreshSig(opts = {}) {
104
127
  await next();
105
128
  };
106
129
  }
130
+ /**
131
+ * Handle an unverifiable request caused by a backend failure (network error,
132
+ * non-2xx, or `degraded` verdict). Fail-closed → 503; fail-open → proceed.
133
+ * @internal
134
+ */
135
+ function handleUnavailable(c, next, failMode, detail) {
136
+ console.error('[TokenForge] verification unavailable:', detail);
137
+ if (failMode === 'open') {
138
+ c.set('tf', { bound: false, trustScore: 0, deviceId: null });
139
+ return next();
140
+ }
141
+ return c.json({
142
+ error: 'verification_unavailable',
143
+ action: 'retry',
144
+ message: 'Session verification is temporarily unavailable',
145
+ }, 503);
146
+ }
107
147
  /** @internal */
108
148
  export function shouldSkip(path, skipPaths) {
109
149
  if (!skipPaths)
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GAAG,wCAAwC,CAAC;AA8BlE;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAA0B;IAE1B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,gBAAgB,CAAC;IACpD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC;IACpD,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,EAAE,CAAC;IAEtD,OAAO,KAAK,EAAE,CAAU,EAAE,IAAU,EAAE,EAAE;QACtC,IAAI,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC;YAAE,OAAO,IAAI,EAAE,CAAC;QAE7D,MAAM,OAAO,GAAG;YACd,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,IAAI;YACjD,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,IAAI;YACzC,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,IAAI;YACjD,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,IAAI;SACjD,CAAC;QAEF,uCAAuC;QACvC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,iBAAiB,EAAE;YACnD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,OAAO,CAAC,MAAM,EAAE;gBACzC,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI;gBAChB,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM;gBACpB,OAAO;gBACP,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,kBAAkB,CAAC;uBACtC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE;gBAC1C,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE;gBAC/C,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE;aAC5C,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,uCAAuC;YACvC,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YACrD,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;YAC7D,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuB,CAAC;QAE1D,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YAC5B,OAAO,CAAC,CAAC,IAAI,CAAC;gBACZ,KAAK,EAAE,iBAAiB;gBACxB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,UAAU,EAAE,IAAI,CAAC,UAAU;aAC5B,EAAE,GAAG,CAAC,CAAC;QACV,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC9B,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;gBAClE,OAAO,CAAC,CAAC,IAAI,CAAC;oBACZ,KAAK,EAAE,yBAAyB;oBAChC,MAAM,EAAE,kBAAkB;oBAC1B,UAAU,EAAE,IAAI,CAAC,UAAU;iBAC5B,EAAE,GAAG,CAAC,CAAC;YACV,CAAC;YACD,oDAAoD;QACtD,CAAC;QAED,IACE,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC;eAC1D,IAAI,CAAC,UAAU,GAAG,EAAE,EACvB,CAAC;YACD,OAAO,CAAC,CAAC,IAAI,CAAC;gBACZ,KAAK,EAAE,yBAAyB;gBAChC,MAAM,EAAE,kBAAkB;gBAC1B,UAAU,EAAE,IAAI,CAAC,UAAU;aAC5B,EAAE,GAAG,CAAC,CAAC;QACV,CAAC;QAED,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE;YACV,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAC,CAAC;QAEH,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,OAAmC,EAAE;IACnE,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC;IACrC,OAAO,KAAK,EAAE,CAAU,EAAE,IAAU,EAAE,EAAE;QACtC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAwC,CAAC;QAC9D,MAAM,KAAK,GAAG,EAAE,EAAE,UAAU,IAAI,CAAC,CAAC;QAClC,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;YAChB,OAAO,CAAC,CAAC,IAAI,CAAC;gBACZ,KAAK,EAAE,yBAAyB;gBAChC,MAAM,EAAE,kBAAkB;gBAC1B,UAAU,EAAE,KAAK;aAClB,EAAE,GAAG,CAAC,CAAC;QACV,CAAC;QACD,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED,gBAAgB;AAChB,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,SAAoB;IAC3D,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7B,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;QAChC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACxE,OAAO,IAAI,KAAK,OAAO,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,gBAAgB;AAChB,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,MAAc,EACd,YAAuB;IAEvB,IAAI,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAChC,MAAM,GAAG,GAAG,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC;IAChC,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE;QAC9B,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC5B,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,QAAQ,KAAK,MAAM,IAAI,CAAC,MAAM;gBAAE,OAAO,KAAK,CAAC;YACjD,OAAO,IAAI,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3E,CAAC;QACD,OAAO,GAAG,KAAK,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"../src/","sources":["server/middleware.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GAAG,wCAAwC,CAAC;AAuClE;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAA0B;IAE1B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,gBAAgB,CAAC;IACpD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC;IACpD,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,EAAE,CAAC;IACtD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAC;IAE9C,OAAO,KAAK,EAAE,CAAU,EAAE,IAAU,EAAE,EAAE;QACtC,IAAI,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC;YAAE,OAAO,IAAI,EAAE,CAAC;QAE7D,MAAM,OAAO,GAAG;YACd,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,IAAI;YACjD,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,IAAI;YACzC,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,IAAI;YACjD,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,IAAI;SACjD,CAAC;QAEF,uEAAuE;QACvE,wEAAwE;QACxE,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YACpF,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;gBACxB,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC7D,OAAO,IAAI,EAAE,CAAC;YAChB,CAAC;YACD,OAAO,CAAC,CAAC,IAAI,CAAC;gBACZ,KAAK,EAAE,yBAAyB;gBAChC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,6CAA6C;aACvD,EAAE,GAAG,CAAC,CAAC;QACV,CAAC;QAED,uCAAuC;QACvC,IAAI,GAAa,CAAC;QAClB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,iBAAiB,EAAE;gBAC7C,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,OAAO,CAAC,MAAM,EAAE;oBACzC,cAAc,EAAE,kBAAkB;iBACnC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI;oBAChB,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM;oBACpB,OAAO;oBACP,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,kBAAkB,CAAC;2BACtC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE;oBAC1C,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE;oBAC/C,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE;iBAC5C,CAAC;aACH,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qDAAqD;YACrD,OAAO,iBAAiB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,OAAO,iBAAiB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACtE,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuB,CAAC;QAE1D,uEAAuE;QACvE,gEAAgE;QAChE,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAC/B,OAAO,iBAAiB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;QAC/D,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YAC5B,OAAO,CAAC,CAAC,IAAI,CAAC;gBACZ,KAAK,EAAE,iBAAiB;gBACxB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,UAAU,EAAE,IAAI,CAAC,UAAU;aAC5B,EAAE,GAAG,CAAC,CAAC;QACV,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC9B,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;gBAClE,OAAO,CAAC,CAAC,IAAI,CAAC;oBACZ,KAAK,EAAE,yBAAyB;oBAChC,MAAM,EAAE,kBAAkB;oBAC1B,UAAU,EAAE,IAAI,CAAC,UAAU;iBAC5B,EAAE,GAAG,CAAC,CAAC;YACV,CAAC;YACD,oDAAoD;QACtD,CAAC;QAED,IACE,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC;eAC1D,IAAI,CAAC,UAAU,GAAG,EAAE,EACvB,CAAC;YACD,OAAO,CAAC,CAAC,IAAI,CAAC;gBACZ,KAAK,EAAE,yBAAyB;gBAChC,MAAM,EAAE,kBAAkB;gBAC1B,UAAU,EAAE,IAAI,CAAC,UAAU;aAC5B,EAAE,GAAG,CAAC,CAAC;QACV,CAAC;QAED,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE;YACV,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAC,CAAC;QAEH,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,OAAmC,EAAE;IACnE,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC;IACrC,OAAO,KAAK,EAAE,CAAU,EAAE,IAAU,EAAE,EAAE;QACtC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAwC,CAAC;QAC9D,MAAM,KAAK,GAAG,EAAE,EAAE,UAAU,IAAI,CAAC,CAAC;QAClC,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;YAChB,OAAO,CAAC,CAAC,IAAI,CAAC;gBACZ,KAAK,EAAE,yBAAyB;gBAChC,MAAM,EAAE,kBAAkB;gBAC1B,UAAU,EAAE,KAAK;aAClB,EAAE,GAAG,CAAC,CAAC;QACV,CAAC;QACD,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,iBAAiB,CACxB,CAAU,EAAE,IAAU,EAAE,QAA2B,EAAE,MAAc;IAEnE,OAAO,CAAC,KAAK,CAAC,wCAAwC,EAAE,MAAM,CAAC,CAAC;IAChE,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC;IACD,OAAO,CAAC,CAAC,IAAI,CAAC;QACZ,KAAK,EAAE,0BAA0B;QACjC,MAAM,EAAE,OAAO;QACf,OAAO,EAAE,iDAAiD;KAC3D,EAAE,GAAG,CAAC,CAAC;AACV,CAAC;AAED,gBAAgB;AAChB,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,SAAoB;IAC3D,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7B,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;QAChC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACxE,OAAO,IAAI,KAAK,OAAO,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,gBAAgB;AAChB,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,MAAc,EACd,YAAuB;IAEvB,IAAI,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAChC,MAAM,GAAG,GAAG,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC;IAChC,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE;QAC9B,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC5B,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,QAAQ,KAAK,MAAM,IAAI,CAAC,MAAM;gBAAE,OAAO,KAAK,CAAC;YACjD,OAAO,IAAI,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3E,CAAC;QACD,OAAO,GAAG,KAAK,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -20,6 +20,8 @@ export interface OidcClaims {
20
20
  email?: string;
21
21
  name?: string;
22
22
  groups?: string[];
23
+ /** Google Workspace hosted domain. Absent for personal Google accounts. */
24
+ hd?: string;
23
25
  [key: string]: unknown;
24
26
  }
25
27
  export interface JwksKey {
@@ -34,8 +36,21 @@ export interface JwksKey {
34
36
  y?: string;
35
37
  }
36
38
  export interface VerifyOidcOptions {
37
- expectedIssuer: string;
39
+ /**
40
+ * Accepted `iss` value(s). A list is required because Google issues ID tokens
41
+ * with either `https://accounts.google.com` or the bare `accounts.google.com`
42
+ * — both documented as valid — so strict equality against one form rejects
43
+ * a large share of legitimate Google tokens.
44
+ */
45
+ expectedIssuer: string | string[];
38
46
  expectedAudience: string;
47
+ /**
48
+ * Required Google Workspace hosted domain (`hd` claim). When set, a token
49
+ * without a matching `hd` is rejected: a Workspace app that skips this check
50
+ * accepts any Google account, including personal gmail.com, as a verified
51
+ * corporate identity.
52
+ */
53
+ expectedHostedDomain?: string;
39
54
  jwks: {
40
55
  keys: JwksKey[];
41
56
  };