@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,101 @@
1
+ /**
2
+ * Per-action step-up policy (Sprint 39).
3
+ *
4
+ * Tenant-scoped JSON config describing which routes require fresh PoP
5
+ * (a recent action-signing JWS) vs WebAuthn re-prompt vs nothing
6
+ * special. Will eventually live on `tf_tenants.stepUpActions` (per
7
+ * CISCO-dua spec) — schema migration pending. The pure logic here
8
+ * is independent of where the actions blob is stored.
9
+ *
10
+ * Shape (validated by `parseStepUpActions`):
11
+ * [
12
+ * { path: '/checkout', requireFreshSig: true, freshSigMaxAgeSec: 60 },
13
+ * { path: '/admin/*', requireFreshSig: true, freshSigMaxAgeSec: 30, requireWebAuthn: true },
14
+ * { path: '/health', requireFreshSig: false, requireWebAuthn: false }
15
+ * ]
16
+ *
17
+ * Matching rules:
18
+ * - exact path match wins over glob-prefix
19
+ * - longest glob-prefix wins among prefix-matches
20
+ * - no match → default verdict (no step-up, no WebAuthn, default 60s)
21
+ */
22
+ const DEFAULT_FRESH_SIG_MAX_AGE_SEC = 60;
23
+ const MAX_ACTIONS = 50;
24
+ const MIN_FRESH_SIG_SEC = 5;
25
+ const MAX_FRESH_SIG_SEC = 600;
26
+ const DEFAULT_VERDICT = {
27
+ matched: false,
28
+ requireFreshSig: false,
29
+ freshSigMaxAgeSec: DEFAULT_FRESH_SIG_MAX_AGE_SEC,
30
+ requireWebAuthn: false,
31
+ };
32
+ export function parseStepUpActions(raw) {
33
+ let parsed;
34
+ try {
35
+ parsed = JSON.parse(raw);
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ if (!Array.isArray(parsed))
41
+ return null;
42
+ if (parsed.length > MAX_ACTIONS)
43
+ return null;
44
+ const actions = [];
45
+ for (const candidate of parsed) {
46
+ if (!candidate || typeof candidate !== 'object')
47
+ return null;
48
+ const c = candidate;
49
+ if (typeof c.path !== 'string' || c.path.length === 0 || c.path.length > 256)
50
+ return null;
51
+ const action = { path: c.path };
52
+ if (c.requireFreshSig !== undefined) {
53
+ if (typeof c.requireFreshSig !== 'boolean')
54
+ return null;
55
+ action.requireFreshSig = c.requireFreshSig;
56
+ }
57
+ if (c.freshSigMaxAgeSec !== undefined) {
58
+ if (typeof c.freshSigMaxAgeSec !== 'number' || !Number.isInteger(c.freshSigMaxAgeSec))
59
+ return null;
60
+ if (c.freshSigMaxAgeSec < MIN_FRESH_SIG_SEC || c.freshSigMaxAgeSec > MAX_FRESH_SIG_SEC)
61
+ return null;
62
+ action.freshSigMaxAgeSec = c.freshSigMaxAgeSec;
63
+ }
64
+ if (c.requireWebAuthn !== undefined) {
65
+ if (typeof c.requireWebAuthn !== 'boolean')
66
+ return null;
67
+ action.requireWebAuthn = c.requireWebAuthn;
68
+ }
69
+ actions.push(action);
70
+ }
71
+ return actions;
72
+ }
73
+ export function evaluateStepUpPolicy(actions, requestPath) {
74
+ // Exact match wins outright.
75
+ for (const a of actions) {
76
+ if (!a.path.endsWith('*') && a.path === requestPath)
77
+ return verdictFrom(a, true);
78
+ }
79
+ // Otherwise, longest matching glob-prefix wins.
80
+ let best = null;
81
+ let bestLen = -1;
82
+ for (const a of actions) {
83
+ if (!a.path.endsWith('*'))
84
+ continue;
85
+ const prefix = a.path.slice(0, -1);
86
+ if (requestPath.startsWith(prefix) && prefix.length > bestLen) {
87
+ best = a;
88
+ bestLen = prefix.length;
89
+ }
90
+ }
91
+ return best ? verdictFrom(best, true) : DEFAULT_VERDICT;
92
+ }
93
+ function verdictFrom(a, matched) {
94
+ return {
95
+ matched,
96
+ requireFreshSig: a.requireFreshSig ?? false,
97
+ freshSigMaxAgeSec: a.freshSigMaxAgeSec ?? DEFAULT_FRESH_SIG_MAX_AGE_SEC,
98
+ requireWebAuthn: a.requireWebAuthn ?? false,
99
+ };
100
+ }
101
+ //# sourceMappingURL=step-up-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-up-policy.js","sourceRoot":"../src/","sources":["server/step-up-policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export type { TokenForgeStorage, StepUpChallengeRecord } from './interface.js';
2
+ export { MemoryStorage } from './memory.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"../src/","sources":["server/storage/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { MemoryStorage } from './memory.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"../src/","sources":["server/storage/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,73 @@
1
+ import type { DeviceSession, SecurityEvent } from '../../shared/types.js';
2
+ /**
3
+ * Storage interface for TokenForge server.
4
+ * Implement this to use any database/cache backend.
5
+ */
6
+ export interface TokenForgeStorage {
7
+ /** Find an active (non-revoked) session by sessionId + deviceId */
8
+ getSession(sessionId: string, deviceId: string): Promise<DeviceSession | null>;
9
+ /** Create a new device session */
10
+ createSession(session: DeviceSession): Promise<void>;
11
+ /** Update trust score and last_verified_at for a session */
12
+ updateTrustScore(deviceId: string, trustScore: number): Promise<void>;
13
+ /** Store the DBSC bound cookie hash + expiry for a device session */
14
+ updateBoundCookieHash(deviceId: string, hash: string, expiresAt: string): Promise<void>;
15
+ /** Mark a session as revoked */
16
+ revokeSession(deviceId: string, reason: string): Promise<void>;
17
+ /** Revoke all sessions for a user (called before re-binding) */
18
+ revokeUserSessions(userId: string): Promise<void>;
19
+ /** List a user's sessions (most recent first) */
20
+ listUserSessions(userId: string, limit: number): Promise<DeviceSession[]>;
21
+ /** Restore trust score to 100 after step-up */
22
+ restoreTrust(deviceId: string, userId: string): Promise<void>;
23
+ /** Check if nonce has been used */
24
+ hasNonce(nonce: string): Promise<boolean>;
25
+ /** Store a used nonce with TTL in seconds */
26
+ storeNonce(nonce: string, ttlSeconds: number): Promise<void>;
27
+ /** Insert a security event record */
28
+ logEvent(event: SecurityEvent & {
29
+ id: string;
30
+ }): Promise<void>;
31
+ /** List security events for a user (for dashboard) */
32
+ listEvents(userId: string, limit: number, offset: number): Promise<(SecurityEvent & {
33
+ id: string;
34
+ created_at: string;
35
+ })[]>;
36
+ /** Create a step-up challenge */
37
+ createChallenge(challenge: StepUpChallengeRecord): Promise<void>;
38
+ /** Get a pending challenge by ID + userId */
39
+ getChallenge(challengeId: string, userId: string): Promise<StepUpChallengeRecord | null>;
40
+ /** Update challenge status */
41
+ updateChallengeStatus(challengeId: string, status: string, completedAt?: string): Promise<void>;
42
+ /** Count recent challenges for rate limiting */
43
+ countRecentChallenges(userId: string, windowMinutes: number): Promise<number>;
44
+ /** Retrieve the activity histogram for a user (null if none stored). */
45
+ getActivityHistogram(userId: string): Promise<{
46
+ buckets: number[];
47
+ totalRequests: number;
48
+ } | null>;
49
+ /** Store the updated activity histogram for a user. */
50
+ setActivityHistogram(userId: string, histogram: {
51
+ buckets: number[];
52
+ totalRequests: number;
53
+ }): Promise<void>;
54
+ /** Store OTP code for a challenge with TTL */
55
+ storeOtp(challengeId: string, code: string, ttlSeconds: number): Promise<void>;
56
+ /** Retrieve and consume OTP code */
57
+ getOtp(challengeId: string): Promise<string | null>;
58
+ /** Delete OTP after verification */
59
+ deleteOtp(challengeId: string): Promise<void>;
60
+ }
61
+ /** Step-up challenge stored in database */
62
+ export interface StepUpChallengeRecord {
63
+ id: string;
64
+ sessionId: string;
65
+ userId: string;
66
+ reason: string;
67
+ method: 'totp' | 'email_otp' | 'passkey';
68
+ status: 'pending' | 'completed' | 'expired' | 'failed';
69
+ expiresAt: string;
70
+ createdAt: string;
71
+ completedAt: string | null;
72
+ }
73
+ //# sourceMappingURL=interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"../src/","sources":["server/storage/interface.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.js","sourceRoot":"../src/","sources":["server/storage/interface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,50 @@
1
+ import type { TokenForgeStorage, StepUpChallengeRecord } from './interface.js';
2
+ import type { DeviceSession, SecurityEvent } from '../../shared/types.js';
3
+ /**
4
+ * In-memory storage for testing and development.
5
+ * Not suitable for production — data is lost on restart.
6
+ */
7
+ export declare class MemoryStorage implements TokenForgeStorage {
8
+ sessions: DeviceSession[];
9
+ events: (SecurityEvent & {
10
+ id: string;
11
+ created_at: string;
12
+ })[];
13
+ challenges: StepUpChallengeRecord[];
14
+ private nonces;
15
+ private otps;
16
+ private histograms;
17
+ getSession(sessionId: string, deviceId: string): Promise<DeviceSession | null>;
18
+ createSession(session: DeviceSession): Promise<void>;
19
+ updateTrustScore(deviceId: string, trustScore: number): Promise<void>;
20
+ updateBoundCookieHash(deviceId: string, hash: string, expiresAt: string): Promise<void>;
21
+ revokeSession(deviceId: string, reason: string): Promise<void>;
22
+ revokeUserSessions(userId: string): Promise<void>;
23
+ listUserSessions(userId: string, limit: number): Promise<DeviceSession[]>;
24
+ restoreTrust(deviceId: string, userId: string): Promise<void>;
25
+ hasNonce(nonce: string): Promise<boolean>;
26
+ storeNonce(nonce: string, ttlSeconds: number): Promise<void>;
27
+ logEvent(event: SecurityEvent & {
28
+ id: string;
29
+ }): Promise<void>;
30
+ listEvents(userId: string, limit: number, offset: number): Promise<(SecurityEvent & {
31
+ id: string;
32
+ created_at: string;
33
+ })[]>;
34
+ createChallenge(challenge: StepUpChallengeRecord): Promise<void>;
35
+ getChallenge(challengeId: string, userId: string): Promise<StepUpChallengeRecord | null>;
36
+ updateChallengeStatus(challengeId: string, status: string, completedAt?: string): Promise<void>;
37
+ countRecentChallenges(userId: string, windowMinutes: number): Promise<number>;
38
+ getActivityHistogram(userId: string): Promise<{
39
+ buckets: number[];
40
+ totalRequests: number;
41
+ } | null>;
42
+ setActivityHistogram(userId: string, histogram: {
43
+ buckets: number[];
44
+ totalRequests: number;
45
+ }): Promise<void>;
46
+ storeOtp(challengeId: string, code: string, ttlSeconds: number): Promise<void>;
47
+ getOtp(challengeId: string): Promise<string | null>;
48
+ deleteOtp(challengeId: string): Promise<void>;
49
+ }
50
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"../src/","sources":["server/storage/memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * In-memory storage for testing and development.
3
+ * Not suitable for production — data is lost on restart.
4
+ */
5
+ export class MemoryStorage {
6
+ sessions = [];
7
+ events = [];
8
+ challenges = [];
9
+ nonces = new Map(); // nonce → expiry timestamp
10
+ otps = new Map();
11
+ histograms = new Map();
12
+ async getSession(sessionId, deviceId) {
13
+ return this.sessions.find((s) => s.session_id === sessionId && s.id === deviceId && s.revoked === 0) ?? null;
14
+ }
15
+ async createSession(session) {
16
+ this.sessions.push({ ...session });
17
+ }
18
+ async updateTrustScore(deviceId, trustScore) {
19
+ const s = this.sessions.find((s) => s.id === deviceId);
20
+ if (s) {
21
+ s.trust_score = trustScore;
22
+ s.last_verified_at = new Date().toISOString();
23
+ }
24
+ }
25
+ async updateBoundCookieHash(deviceId, hash, expiresAt) {
26
+ const s = this.sessions.find((s) => s.id === deviceId);
27
+ if (s) {
28
+ s.bound_cookie_hash = hash;
29
+ s.bound_cookie_expires_at = expiresAt;
30
+ }
31
+ }
32
+ async revokeSession(deviceId, reason) {
33
+ const s = this.sessions.find((s) => s.id === deviceId);
34
+ if (s) {
35
+ s.revoked = 1;
36
+ s.revoked_reason = reason;
37
+ }
38
+ }
39
+ async revokeUserSessions(userId) {
40
+ for (const s of this.sessions) {
41
+ if (s.user_id === userId && s.revoked === 0) {
42
+ s.revoked = 1;
43
+ s.revoked_reason = 'rebind';
44
+ }
45
+ }
46
+ }
47
+ async listUserSessions(userId, limit) {
48
+ return this.sessions
49
+ .filter((s) => s.user_id === userId)
50
+ .sort((a, b) => b.bound_at.localeCompare(a.bound_at))
51
+ .slice(0, limit);
52
+ }
53
+ async restoreTrust(deviceId, userId) {
54
+ const s = this.sessions.find((s) => s.id === deviceId && s.user_id === userId);
55
+ if (s)
56
+ s.trust_score = 100;
57
+ }
58
+ async hasNonce(nonce) {
59
+ const expiry = this.nonces.get(nonce);
60
+ if (!expiry)
61
+ return false;
62
+ if (Date.now() > expiry) {
63
+ this.nonces.delete(nonce);
64
+ return false;
65
+ }
66
+ return true;
67
+ }
68
+ async storeNonce(nonce, ttlSeconds) {
69
+ this.nonces.set(nonce, Date.now() + ttlSeconds * 1000);
70
+ }
71
+ async logEvent(event) {
72
+ this.events.push({ ...event, created_at: new Date().toISOString() });
73
+ }
74
+ async listEvents(userId, limit, offset) {
75
+ return this.events
76
+ .filter((e) => e.userId === userId)
77
+ .sort((a, b) => b.created_at.localeCompare(a.created_at))
78
+ .slice(offset, offset + limit);
79
+ }
80
+ async createChallenge(challenge) {
81
+ this.challenges.push({ ...challenge });
82
+ }
83
+ async getChallenge(challengeId, userId) {
84
+ return this.challenges.find((c) => c.id === challengeId && c.userId === userId && c.status === 'pending') ?? null;
85
+ }
86
+ async updateChallengeStatus(challengeId, status, completedAt) {
87
+ const c = this.challenges.find((c) => c.id === challengeId);
88
+ if (c) {
89
+ c.status = status;
90
+ if (completedAt)
91
+ c.completedAt = completedAt;
92
+ }
93
+ }
94
+ async countRecentChallenges(userId, windowMinutes) {
95
+ const cutoff = new Date(Date.now() - windowMinutes * 60 * 1000).toISOString();
96
+ return this.challenges.filter((c) => c.userId === userId && c.createdAt > cutoff).length;
97
+ }
98
+ async getActivityHistogram(userId) {
99
+ return this.histograms.get(`tf:activity:${userId}`) ?? null;
100
+ }
101
+ async setActivityHistogram(userId, histogram) {
102
+ this.histograms.set(`tf:activity:${userId}`, { ...histogram, buckets: [...histogram.buckets] });
103
+ }
104
+ async storeOtp(challengeId, code, ttlSeconds) {
105
+ this.otps.set(challengeId, { code, expiresAt: Date.now() + ttlSeconds * 1000 });
106
+ }
107
+ async getOtp(challengeId) {
108
+ const entry = this.otps.get(challengeId);
109
+ if (!entry)
110
+ return null;
111
+ if (Date.now() > entry.expiresAt) {
112
+ this.otps.delete(challengeId);
113
+ return null;
114
+ }
115
+ return entry.code;
116
+ }
117
+ async deleteOtp(challengeId) {
118
+ this.otps.delete(challengeId);
119
+ }
120
+ }
121
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"../src/","sources":["server/storage/memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Server-side WebAuthn verifier — runs in Cloudflare Workers (Web Crypto only).
3
+ *
4
+ * Limitations (intentional, documented):
5
+ * - We use attestation: 'none' on the client, so attestation statement
6
+ * verification is skipped (only the authData → COSE_Key extraction matters).
7
+ * - Only ES256 (alg -7, ECDSA P-256) keys are accepted on the server. EdDSA
8
+ * is offered to the client for forward-compat but Web Crypto support is
9
+ * spotty in Workers; reject EdDSA at verify time with a clear error.
10
+ * - Counter check is not enforced here. Callers can read the 4-byte counter
11
+ * out of authenticatorData[33..37] if they want to persist + compare.
12
+ */
13
+ export interface AttestationVerifyResult {
14
+ publicKeyJwk: JsonWebKey;
15
+ credentialId: Uint8Array;
16
+ rpIdHash: Uint8Array;
17
+ }
18
+ /** base64url → ArrayBuffer */
19
+ export declare function b64uToBuf(b64url: string): ArrayBuffer;
20
+ /** Uint8Array → base64url (no padding). */
21
+ export declare function bufToB64u(buf: ArrayBuffer | Uint8Array): string;
22
+ /** Parse clientDataJSON and verify challenge + origin. */
23
+ export declare function verifyClientDataJSON(clientDataJSON: string, expectedChallenge: string, expectedOrigin: string, expectedType: 'webauthn.create' | 'webauthn.get'): {
24
+ ok: true;
25
+ } | {
26
+ ok: false;
27
+ reason: string;
28
+ };
29
+ /**
30
+ * Verify a WebAuthn attestation (registration). Returns the extracted
31
+ * public key as JWK plus the credentialId for persistence.
32
+ */
33
+ export declare function verifyWebAuthnAttestation(attestationObjectB64u: string, clientDataJSONB64u: string, expectedChallenge: string, expectedOrigin: string): Promise<AttestationVerifyResult>;
34
+ /**
35
+ * Verify a WebAuthn assertion. The signature is over
36
+ * (authenticatorData || sha256(clientDataJSON)).
37
+ */
38
+ export declare function verifyWebAuthnAssertion(signatureB64u: string, authenticatorDataB64u: string, clientDataJSONB64u: string, publicKeyJwk: JsonWebKey, expectedChallenge: string, expectedOrigin: string): Promise<boolean>;
39
+ /** WebAuthn ECDSA sigs are DER-encoded; Web Crypto wants raw r||s. */
40
+ export declare function derToRawEcdsa(der: Uint8Array): Uint8Array;
41
+ //# sourceMappingURL=webauthn-verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webauthn-verify.d.ts","sourceRoot":"../src/","sources":["server/webauthn-verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,UAAU,CAAC;IACzB,YAAY,EAAE,UAAU,CAAC;IACzB,QAAQ,EAAE,UAAU,CAAC;CACtB;AAED,8BAA8B;AAC9B,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAOrD;AAED,2CAA2C;AAC3C,wBAAgB,SAAS,CAAC,GAAG,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,CAK/D;AAED,0DAA0D;AAC1D,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,MAAM,EACtB,iBAAiB,EAAE,MAAM,EACzB,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,iBAAiB,GAAG,cAAc,GAC/C;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAY9C;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,qBAAqB,EAAE,MAAM,EAC7B,kBAAkB,EAAE,MAAM,EAC1B,iBAAiB,EAAE,MAAM,EACzB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,uBAAuB,CAAC,CAWlC;AAiCD;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,aAAa,EAAE,MAAM,EACrB,qBAAqB,EAAE,MAAM,EAC7B,kBAAkB,EAAE,MAAM,EAC1B,YAAY,EAAE,UAAU,EACxB,iBAAiB,EAAE,MAAM,EACzB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,OAAO,CAAC,CA4BlB;AASD,sEAAsE;AACtE,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,UAAU,CAuBzD"}
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Server-side WebAuthn verifier — runs in Cloudflare Workers (Web Crypto only).
3
+ *
4
+ * Limitations (intentional, documented):
5
+ * - We use attestation: 'none' on the client, so attestation statement
6
+ * verification is skipped (only the authData → COSE_Key extraction matters).
7
+ * - Only ES256 (alg -7, ECDSA P-256) keys are accepted on the server. EdDSA
8
+ * is offered to the client for forward-compat but Web Crypto support is
9
+ * spotty in Workers; reject EdDSA at verify time with a clear error.
10
+ * - Counter check is not enforced here. Callers can read the 4-byte counter
11
+ * out of authenticatorData[33..37] if they want to persist + compare.
12
+ */
13
+ import { decodeCbor } from './cbor.js';
14
+ /** base64url → ArrayBuffer */
15
+ export function b64uToBuf(b64url) {
16
+ const base64 = b64url.replace(/-/g, '+').replace(/_/g, '/');
17
+ const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);
18
+ const binary = atob(padded);
19
+ const bytes = new Uint8Array(binary.length);
20
+ for (let i = 0; i < binary.length; i++)
21
+ bytes[i] = binary.charCodeAt(i);
22
+ return bytes.buffer;
23
+ }
24
+ /** Uint8Array → base64url (no padding). */
25
+ export function bufToB64u(buf) {
26
+ const bytes = buf instanceof Uint8Array ? buf : new Uint8Array(buf);
27
+ let binary = '';
28
+ for (const byte of bytes)
29
+ binary += String.fromCharCode(byte);
30
+ return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
31
+ }
32
+ /** Parse clientDataJSON and verify challenge + origin. */
33
+ export function verifyClientDataJSON(clientDataJSON, expectedChallenge, expectedOrigin, expectedType) {
34
+ let json;
35
+ try {
36
+ const text = new TextDecoder().decode(b64uToBuf(clientDataJSON));
37
+ json = JSON.parse(text);
38
+ }
39
+ catch {
40
+ return { ok: false, reason: 'clientDataJSON malformed' };
41
+ }
42
+ if (json.type !== expectedType)
43
+ return { ok: false, reason: `type mismatch: ${json.type}` };
44
+ if (json.challenge !== expectedChallenge)
45
+ return { ok: false, reason: 'challenge mismatch' };
46
+ if (json.origin !== expectedOrigin)
47
+ return { ok: false, reason: `origin mismatch: ${json.origin}` };
48
+ return { ok: true };
49
+ }
50
+ /**
51
+ * Verify a WebAuthn attestation (registration). Returns the extracted
52
+ * public key as JWK plus the credentialId for persistence.
53
+ */
54
+ export async function verifyWebAuthnAttestation(attestationObjectB64u, clientDataJSONB64u, expectedChallenge, expectedOrigin) {
55
+ const cd = verifyClientDataJSON(clientDataJSONB64u, expectedChallenge, expectedOrigin, 'webauthn.create');
56
+ if (!cd.ok)
57
+ throw new Error(`webauthn.create: ${cd.reason}`);
58
+ const attBuf = new Uint8Array(b64uToBuf(attestationObjectB64u));
59
+ const decoded = decodeCbor(attBuf);
60
+ if (!(decoded instanceof Map))
61
+ throw new Error('attestationObject: not a CBOR map');
62
+ const authData = decoded.get('authData');
63
+ if (!(authData instanceof Uint8Array))
64
+ throw new Error('attestationObject: authData missing');
65
+ return parseAuthDataAttestedCredential(authData);
66
+ }
67
+ /** Pull rpIdHash + credentialId + COSE_Key out of attested authData. */
68
+ function parseAuthDataAttestedCredential(authData) {
69
+ if (authData.length < 37 + 18)
70
+ throw new Error('authData too short for attested credential');
71
+ const rpIdHash = authData.slice(0, 32);
72
+ const flags = authData[32] ?? 0;
73
+ if ((flags & 0x40) === 0)
74
+ throw new Error('authData: AT (attested credential) flag not set');
75
+ // skip signCount (4 bytes), aaguid (16 bytes)
76
+ let p = 37 + 16;
77
+ const credIdLen = ((authData[p] ?? 0) << 8) | (authData[p + 1] ?? 0);
78
+ p += 2;
79
+ const credentialId = authData.slice(p, p + credIdLen);
80
+ p += credIdLen;
81
+ const cosePublicKey = decodeCbor(authData.slice(p));
82
+ return { publicKeyJwk: coseToJwk(cosePublicKey), credentialId, rpIdHash };
83
+ }
84
+ /** Convert a COSE_Key (CBOR map) → JWK. ES256 only. */
85
+ function coseToJwk(cose) {
86
+ if (!(cose instanceof Map))
87
+ throw new Error('COSE_Key: not a map');
88
+ const kty = cose.get(1);
89
+ const alg = cose.get(3);
90
+ if (kty !== 2)
91
+ throw new Error(`COSE_Key kty=${String(kty)} unsupported (need EC2)`);
92
+ if (alg !== -7)
93
+ throw new Error(`COSE_Key alg=${String(alg)} unsupported (need ES256/-7)`);
94
+ const x = cose.get(-2);
95
+ const y = cose.get(-3);
96
+ if (!(x instanceof Uint8Array) || !(y instanceof Uint8Array)) {
97
+ throw new Error('COSE_Key: x/y missing');
98
+ }
99
+ return { kty: 'EC', crv: 'P-256', x: bufToB64u(x), y: bufToB64u(y) };
100
+ }
101
+ /**
102
+ * Verify a WebAuthn assertion. The signature is over
103
+ * (authenticatorData || sha256(clientDataJSON)).
104
+ */
105
+ export async function verifyWebAuthnAssertion(signatureB64u, authenticatorDataB64u, clientDataJSONB64u, publicKeyJwk, expectedChallenge, expectedOrigin) {
106
+ const cd = verifyClientDataJSON(clientDataJSONB64u, expectedChallenge, expectedOrigin, 'webauthn.get');
107
+ if (!cd.ok)
108
+ return false;
109
+ const authData = new Uint8Array(b64uToBuf(authenticatorDataB64u));
110
+ const clientDataBuf = new Uint8Array(b64uToBuf(clientDataJSONB64u));
111
+ const clientDataHash = new Uint8Array(await crypto.subtle.digest('SHA-256', clientDataBuf));
112
+ const signed = new Uint8Array(authData.length + clientDataHash.length);
113
+ signed.set(authData, 0);
114
+ signed.set(clientDataHash, authData.length);
115
+ const key = await crypto.subtle.importKey('jwk', publicKeyJwk, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify']);
116
+ const sigDer = new Uint8Array(b64uToBuf(signatureB64u));
117
+ let sigRaw;
118
+ try {
119
+ sigRaw = derToRawEcdsa(sigDer);
120
+ }
121
+ catch {
122
+ return false;
123
+ }
124
+ try {
125
+ return await crypto.subtle.verify({ name: 'ECDSA', hash: { name: 'SHA-256' } }, key, copyToArrayBuffer(sigRaw), copyToArrayBuffer(signed));
126
+ }
127
+ catch {
128
+ return false;
129
+ }
130
+ }
131
+ /** Copy a Uint8Array into a freshly allocated ArrayBuffer (escapes SharedArrayBuffer typing). */
132
+ function copyToArrayBuffer(src) {
133
+ const out = new ArrayBuffer(src.byteLength);
134
+ new Uint8Array(out).set(src);
135
+ return out;
136
+ }
137
+ /** WebAuthn ECDSA sigs are DER-encoded; Web Crypto wants raw r||s. */
138
+ export function derToRawEcdsa(der) {
139
+ // SEQUENCE 0x30, total len, INTEGER 0x02, rLen, r, INTEGER 0x02, sLen, s
140
+ const at = (i) => {
141
+ const v = der[i];
142
+ if (v === undefined)
143
+ throw new Error('DER: truncated');
144
+ return v;
145
+ };
146
+ if (at(0) !== 0x30)
147
+ throw new Error('DER: not a SEQUENCE');
148
+ let p = 2;
149
+ if (at(1) & 0x80)
150
+ p = 2 + (at(1) & 0x7f);
151
+ if (at(p) !== 0x02)
152
+ throw new Error('DER: r not INTEGER');
153
+ let rLen = at(p + 1);
154
+ let rStart = p + 2;
155
+ if (rLen > 32) {
156
+ rStart += rLen - 32;
157
+ rLen = 32;
158
+ }
159
+ const r = der.slice(rStart, rStart + rLen);
160
+ p = rStart + rLen;
161
+ if (at(p) !== 0x02)
162
+ throw new Error('DER: s not INTEGER');
163
+ let sLen = at(p + 1);
164
+ let sStart = p + 2;
165
+ if (sLen > 32) {
166
+ sStart += sLen - 32;
167
+ sLen = 32;
168
+ }
169
+ const s = der.slice(sStart, sStart + sLen);
170
+ const raw = new Uint8Array(64);
171
+ raw.set(r, 32 - r.length);
172
+ raw.set(s, 64 - s.length);
173
+ return raw;
174
+ }
175
+ //# sourceMappingURL=webauthn-verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webauthn-verify.js","sourceRoot":"../src/","sources":["server/webauthn-verify.ts"],"names":[],"mappings":"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"}