@frontmcp/auth 1.10.16 → 1.11.0-rc.1

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 (175) hide show
  1. package/browser/authorities/authorities.context.mjs +115 -0
  2. package/browser/authorities/authorities.engine.mjs +272 -0
  3. package/browser/authorities/authorities.errors.mjs +59 -0
  4. package/browser/authorities/authorities.evaluator.mjs +225 -0
  5. package/browser/authorities/authorities.metadata-augment.mjs +18 -0
  6. package/browser/authorities/authorities.registry.mjs +112 -0
  7. package/browser/authorities/authorities.schema.mjs +92 -0
  8. package/browser/authorities/authorities.scope-mapping.mjs +30 -0
  9. package/browser/authorities/authorities.validation.mjs +208 -0
  10. package/browser/authorities/index.mjs +43 -0
  11. package/browser/authorization/authorization.class.mjs +252 -0
  12. package/browser/authorization/authorization.types.mjs +64 -0
  13. package/browser/authorization/dcr-client.registry.mjs +222 -0
  14. package/browser/authorization/index.mjs +41 -0
  15. package/browser/authorization/orchestrated.accessor.mjs +86 -0
  16. package/browser/authorization/orchestrated.authorization.mjs +289 -0
  17. package/browser/authorization/public.authorization.mjs +124 -0
  18. package/browser/authorization/transparent.authorization.mjs +130 -0
  19. package/browser/cimd/cimd-redis.cache.mjs +231 -0
  20. package/browser/cimd/cimd.cache.mjs +215 -0
  21. package/browser/cimd/cimd.errors.mjs +142 -0
  22. package/browser/cimd/cimd.logger.mjs +15 -0
  23. package/browser/cimd/cimd.service.mjs +376 -0
  24. package/browser/cimd/cimd.types.mjs +228 -0
  25. package/browser/cimd/cimd.validator.mjs +217 -0
  26. package/browser/cimd/index.mjs +63 -0
  27. package/browser/common/auth-logger.interface.mjs +14 -0
  28. package/browser/common/index.mjs +6 -0
  29. package/browser/common/jwt.types.mjs +38 -0
  30. package/browser/common/session-user.types.mjs +0 -0
  31. package/browser/common/session.types.mjs +58 -0
  32. package/browser/common/zod-utils.mjs +0 -0
  33. package/browser/consent/consent.types.mjs +94 -0
  34. package/browser/consent/index.mjs +17 -0
  35. package/browser/context/frontmcp-auth-context.factory.mjs +29 -0
  36. package/browser/context/frontmcp-auth-context.impl.mjs +112 -0
  37. package/browser/context/index.mjs +7 -0
  38. package/browser/detection/auth-provider-detection.mjs +176 -0
  39. package/browser/detection/index.mjs +19 -0
  40. package/browser/errors/auth-internal.error.mjs +42 -0
  41. package/browser/errors/auth-internal.errors.mjs +150 -0
  42. package/browser/errors/index.mjs +53 -0
  43. package/browser/fetch/fetch-credential-middleware.mjs +91 -0
  44. package/browser/fetch/index.mjs +19 -0
  45. package/browser/index.mjs +573 -0
  46. package/browser/jwks/index.mjs +9 -0
  47. package/browser/jwks/jose.loader.mjs +28 -0
  48. package/browser/jwks/jwks.service.mjs +521 -0
  49. package/browser/jwks/jwks.utils.mjs +33 -0
  50. package/browser/options/app-auth.schema.mjs +18 -0
  51. package/browser/options/index.mjs +89 -0
  52. package/browser/options/orchestrated.schema.mjs +128 -0
  53. package/browser/options/public.schema.mjs +39 -0
  54. package/browser/options/schema.mjs +16 -0
  55. package/browser/options/shared.schemas.mjs +506 -0
  56. package/browser/options/static.schema.mjs +41 -0
  57. package/browser/options/transparent.schema.mjs +43 -0
  58. package/browser/options/utils.mjs +47 -0
  59. package/browser/people/index.mjs +15 -0
  60. package/browser/people/loopback-token.auth.mjs +115 -0
  61. package/browser/people/people-auth.options.mjs +37 -0
  62. package/browser/session/authorization-vault.mjs +279 -0
  63. package/browser/session/authorization.store.mjs +336 -0
  64. package/browser/session/consent.store.mjs +34 -0
  65. package/browser/session/credential-resume-link.mjs +43 -0
  66. package/browser/session/credentials-accessor.impl.mjs +54 -0
  67. package/browser/session/credentials-accessor.mjs +5 -0
  68. package/browser/session/encrypted-authorization-vault.mjs +405 -0
  69. package/browser/session/federated-auth.session.mjs +176 -0
  70. package/browser/session/incremental-auth-ticket.mjs +42 -0
  71. package/browser/session/index.mjs +266 -0
  72. package/browser/session/orchestrated-token.crypto.mjs +47 -0
  73. package/browser/session/orchestrated-token.store.mjs +286 -0
  74. package/browser/session/pending-login.mjs +37 -0
  75. package/browser/session/redis-session.store.mjs +255 -0
  76. package/browser/session/secure-store-accessor.impl.mjs +78 -0
  77. package/browser/session/secure-store-accessor.mjs +5 -0
  78. package/browser/session/secure-store-backends.mjs +111 -0
  79. package/browser/session/secure-store.factory.mjs +77 -0
  80. package/browser/session/session-credential-vault.factory.mjs +18 -0
  81. package/browser/session/session-credential-vault.mjs +243 -0
  82. package/browser/session/session-crypto.mjs +39 -0
  83. package/browser/session/session-rate-limiter.mjs +126 -0
  84. package/browser/session/session.transport.mjs +16 -0
  85. package/browser/session/signin-binding.mjs +30 -0
  86. package/browser/session/storage/in-memory-authorization-vault.mjs +28 -0
  87. package/browser/session/storage/index.mjs +14 -0
  88. package/browser/session/storage/storage-authorization-vault.mjs +301 -0
  89. package/browser/session/storage/storage-token-store.mjs +83 -0
  90. package/browser/session/storage-authorization.store.mjs +134 -0
  91. package/browser/session/storage-consent.store.mjs +33 -0
  92. package/browser/session/storage-federated-auth.session.mjs +55 -0
  93. package/browser/session/storage-orchestrated-token.store.mjs +197 -0
  94. package/browser/session/token-storage.factory.mjs +58 -0
  95. package/browser/session/token.refresh.mjs +36 -0
  96. package/browser/session/token.vault.mjs +61 -0
  97. package/browser/session/transport-session.types.mjs +108 -0
  98. package/browser/session/utils/auth-token.utils.mjs +58 -0
  99. package/browser/session/utils/index.mjs +23 -0
  100. package/browser/session/utils/session-crypto.utils.mjs +49 -0
  101. package/browser/session/utils/tiny-ttl-cache.mjs +46 -0
  102. package/browser/session/vault-encryption.mjs +187 -0
  103. package/browser/session/vercel-kv-session.store.mjs +230 -0
  104. package/browser/shared-server/index.mjs +17 -0
  105. package/browser/shared-server/shared-server-identity.mjs +55 -0
  106. package/browser/ui/base-layout.mjs +184 -0
  107. package/browser/ui/index.mjs +51 -0
  108. package/browser/ui/local-login.helper.mjs +46 -0
  109. package/browser/ui/templates.mjs +482 -0
  110. package/browser/utils/audience.validator.mjs +110 -0
  111. package/browser/utils/authorization-id.utils.mjs +10 -0
  112. package/browser/utils/index.mjs +45 -0
  113. package/browser/utils/redirect-uri.utils.mjs +22 -0
  114. package/browser/utils/scope-grant.utils.mjs +36 -0
  115. package/browser/utils/token-scopes.utils.mjs +12 -0
  116. package/browser/utils/www-authenticate.utils.mjs +164 -0
  117. package/browser/vault/auth-providers.accessor.impl.mjs +236 -0
  118. package/browser/vault/auth-providers.accessor.mjs +5 -0
  119. package/browser/vault/auth-providers.registry.mjs +140 -0
  120. package/browser/vault/auth-providers.types.mjs +47 -0
  121. package/browser/vault/auth-providers.vault.mjs +206 -0
  122. package/browser/vault/credential-cache.mjs +187 -0
  123. package/browser/vault/credential-helpers.mjs +15 -0
  124. package/browser/vault/credential-loaders/eager-loader.mjs +91 -0
  125. package/browser/vault/credential-loaders/lazy-loader.mjs +120 -0
  126. package/browser/vault/index.mjs +35 -0
  127. package/cimd/cimd.types.d.ts +1 -1
  128. package/cimd/cimd.types.d.ts.map +1 -1
  129. package/common/jwt.types.d.ts +1 -1
  130. package/common/jwt.types.d.ts.map +1 -1
  131. package/common/session.types.d.ts +9 -1
  132. package/common/session.types.d.ts.map +1 -1
  133. package/consent/consent.types.d.ts +1 -1
  134. package/consent/consent.types.d.ts.map +1 -1
  135. package/detection/auth-provider-detection.d.ts +1 -1
  136. package/detection/auth-provider-detection.d.ts.map +1 -1
  137. package/errors/auth-internal.errors.d.ts +6 -0
  138. package/errors/auth-internal.errors.d.ts.map +1 -1
  139. package/errors/index.d.ts +1 -1
  140. package/errors/index.d.ts.map +1 -1
  141. package/esm/index.mjs +2 -2
  142. package/esm/package.json +21 -5
  143. package/index.d.ts +5 -1
  144. package/index.d.ts.map +1 -1
  145. package/index.js +2 -2
  146. package/options/app-auth.schema.d.ts +19 -1
  147. package/options/app-auth.schema.d.ts.map +1 -1
  148. package/options/orchestrated.schema.d.ts +37 -1
  149. package/options/orchestrated.schema.d.ts.map +1 -1
  150. package/options/public.schema.d.ts +1 -1
  151. package/options/public.schema.d.ts.map +1 -1
  152. package/options/schema.d.ts +19 -1
  153. package/options/schema.d.ts.map +1 -1
  154. package/options/shared.schemas.d.ts +10 -14
  155. package/options/shared.schemas.d.ts.map +1 -1
  156. package/options/static.schema.d.ts +1 -1
  157. package/options/static.schema.d.ts.map +1 -1
  158. package/options/transparent.schema.d.ts +1 -1
  159. package/options/transparent.schema.d.ts.map +1 -1
  160. package/package.json +21 -5
  161. package/people/index.d.ts +3 -0
  162. package/people/index.d.ts.map +1 -0
  163. package/people/loopback-token.auth.d.ts +75 -0
  164. package/people/loopback-token.auth.d.ts.map +1 -0
  165. package/people/people-auth.options.d.ts +36 -0
  166. package/people/people-auth.options.d.ts.map +1 -0
  167. package/session/authorization-vault.d.ts +1 -1
  168. package/session/authorization-vault.d.ts.map +1 -1
  169. package/session/encrypted-authorization-vault.d.ts +1 -1
  170. package/session/encrypted-authorization-vault.d.ts.map +1 -1
  171. package/session/secure-store.factory.d.ts.map +1 -1
  172. package/shared-server/index.d.ts +3 -0
  173. package/shared-server/index.d.ts.map +1 -0
  174. package/shared-server/shared-server-identity.d.ts +26 -0
  175. package/shared-server/shared-server-identity.d.ts.map +1 -0
@@ -0,0 +1,301 @@
1
+ // libs/auth/src/session/storage/storage-authorization-vault.ts
2
+ import { randomUUID, TypedStorage } from "@frontmcp/utils";
3
+ import { authorizationVaultEntrySchema } from "../authorization-vault.mjs";
4
+ var StorageAuthorizationVault = class {
5
+ storage;
6
+ namespace;
7
+ pendingAuthTtlMs;
8
+ constructor(storage, options = {}) {
9
+ this.namespace = options.namespace ?? "vault";
10
+ this.pendingAuthTtlMs = options.pendingAuthTtlMs ?? 10 * 60 * 1e3;
11
+ const namespacedStorage = this.isNamespacedStorage(storage) ? storage.namespace(this.namespace) : storage;
12
+ this.storage = new TypedStorage(namespacedStorage, {
13
+ schema: options.validateOnRead ? authorizationVaultEntrySchema : void 0,
14
+ throwOnInvalid: false
15
+ });
16
+ }
17
+ // ============================================
18
+ // Core CRUD Methods
19
+ // ============================================
20
+ async create(params) {
21
+ const now = Date.now();
22
+ const entry = {
23
+ id: randomUUID(),
24
+ userSub: params.userSub,
25
+ userEmail: params.userEmail,
26
+ userName: params.userName,
27
+ clientId: params.clientId,
28
+ createdAt: now,
29
+ lastAccessAt: now,
30
+ appCredentials: {},
31
+ consent: params.consent,
32
+ federated: params.federated,
33
+ pendingAuths: [],
34
+ authorizedAppIds: params.authorizedAppIds ?? [],
35
+ skippedAppIds: params.skippedAppIds ?? []
36
+ };
37
+ await this.storage.set(this.key(entry.id), entry);
38
+ return entry;
39
+ }
40
+ async get(id) {
41
+ return this.storage.get(this.key(id));
42
+ }
43
+ async update(id, updates) {
44
+ const entry = await this.get(id);
45
+ if (!entry) {
46
+ console.warn(`[StorageAuthorizationVault] Update failed: vault entry not found for id ${id}`);
47
+ return;
48
+ }
49
+ const updated = {
50
+ ...entry,
51
+ ...updates,
52
+ lastAccessAt: Date.now()
53
+ };
54
+ await this.storage.set(this.key(id), updated);
55
+ }
56
+ async delete(id) {
57
+ await this.storage.delete(this.key(id));
58
+ }
59
+ // ============================================
60
+ // Consent Methods
61
+ // ============================================
62
+ async updateConsent(vaultId, consent) {
63
+ const entry = await this.get(vaultId);
64
+ if (!entry) return;
65
+ entry.consent = consent;
66
+ entry.lastAccessAt = Date.now();
67
+ await this.storage.set(this.key(vaultId), entry);
68
+ }
69
+ // ============================================
70
+ // App Authorization Methods
71
+ // ============================================
72
+ async authorizeApp(vaultId, appId) {
73
+ const entry = await this.get(vaultId);
74
+ if (!entry) return;
75
+ entry.skippedAppIds = entry.skippedAppIds.filter((id) => id !== appId);
76
+ if (!entry.authorizedAppIds.includes(appId)) {
77
+ entry.authorizedAppIds.push(appId);
78
+ }
79
+ entry.lastAccessAt = Date.now();
80
+ await this.storage.set(this.key(vaultId), entry);
81
+ }
82
+ async isAppAuthorized(vaultId, appId) {
83
+ const entry = await this.get(vaultId);
84
+ if (!entry) return false;
85
+ return entry.authorizedAppIds.includes(appId);
86
+ }
87
+ // ============================================
88
+ // Pending Auth Methods
89
+ // ============================================
90
+ async createPendingAuth(vaultId, params) {
91
+ const entry = await this.get(vaultId);
92
+ if (!entry) {
93
+ throw new Error(`Vault not found: ${vaultId}`);
94
+ }
95
+ const now = Date.now();
96
+ const pendingAuth = {
97
+ id: randomUUID(),
98
+ appId: params.appId,
99
+ toolId: params.toolId,
100
+ authUrl: params.authUrl,
101
+ requiredScopes: params.requiredScopes,
102
+ elicitId: params.elicitId,
103
+ createdAt: now,
104
+ expiresAt: now + (params.ttlMs ?? this.pendingAuthTtlMs),
105
+ status: "pending"
106
+ };
107
+ entry.pendingAuths.push(pendingAuth);
108
+ entry.lastAccessAt = now;
109
+ await this.storage.set(this.key(vaultId), entry);
110
+ return pendingAuth;
111
+ }
112
+ async getPendingAuth(vaultId, pendingAuthId) {
113
+ const entry = await this.get(vaultId);
114
+ if (!entry) return null;
115
+ const pendingAuth = entry.pendingAuths.find((p) => p.id === pendingAuthId);
116
+ if (!pendingAuth) return null;
117
+ if (Date.now() > pendingAuth.expiresAt && pendingAuth.status === "pending") {
118
+ pendingAuth.status = "expired";
119
+ await this.storage.set(this.key(vaultId), entry);
120
+ }
121
+ return pendingAuth;
122
+ }
123
+ async completePendingAuth(vaultId, pendingAuthId) {
124
+ const entry = await this.get(vaultId);
125
+ if (!entry) return;
126
+ const pendingAuth = entry.pendingAuths.find((p) => p.id === pendingAuthId);
127
+ if (pendingAuth) {
128
+ pendingAuth.status = "completed";
129
+ entry.skippedAppIds = entry.skippedAppIds.filter((id) => id !== pendingAuth.appId);
130
+ if (!entry.authorizedAppIds.includes(pendingAuth.appId)) {
131
+ entry.authorizedAppIds.push(pendingAuth.appId);
132
+ }
133
+ entry.lastAccessAt = Date.now();
134
+ await this.storage.set(this.key(vaultId), entry);
135
+ }
136
+ }
137
+ async cancelPendingAuth(vaultId, pendingAuthId) {
138
+ const entry = await this.get(vaultId);
139
+ if (!entry) return;
140
+ const pendingAuth = entry.pendingAuths.find((p) => p.id === pendingAuthId);
141
+ if (pendingAuth) {
142
+ pendingAuth.status = "cancelled";
143
+ await this.storage.set(this.key(vaultId), entry);
144
+ }
145
+ }
146
+ async getPendingAuths(vaultId) {
147
+ const entry = await this.get(vaultId);
148
+ if (!entry) return [];
149
+ const now = Date.now();
150
+ let updated = false;
151
+ const pending = entry.pendingAuths.filter((p) => {
152
+ if (now > p.expiresAt && p.status === "pending") {
153
+ p.status = "expired";
154
+ updated = true;
155
+ }
156
+ return p.status === "pending";
157
+ });
158
+ if (updated) {
159
+ await this.storage.set(this.key(vaultId), entry);
160
+ }
161
+ return pending;
162
+ }
163
+ // ============================================
164
+ // App Credential Methods
165
+ // ============================================
166
+ credentialKey(appId, providerId) {
167
+ return `${appId}:${providerId}`;
168
+ }
169
+ async addAppCredential(vaultId, credential) {
170
+ const entry = await this.get(vaultId);
171
+ if (!entry) return;
172
+ const shouldStore = await this.shouldStoreCredential(vaultId, credential.appId);
173
+ if (!shouldStore) {
174
+ return;
175
+ }
176
+ const key = this.credentialKey(credential.appId, credential.providerId);
177
+ entry.appCredentials[key] = credential;
178
+ entry.lastAccessAt = Date.now();
179
+ await this.storage.set(this.key(vaultId), entry);
180
+ }
181
+ async removeAppCredential(vaultId, appId, providerId) {
182
+ const entry = await this.get(vaultId);
183
+ if (!entry) return;
184
+ const key = this.credentialKey(appId, providerId);
185
+ delete entry.appCredentials[key];
186
+ entry.lastAccessAt = Date.now();
187
+ await this.storage.set(this.key(vaultId), entry);
188
+ }
189
+ async getAppCredentials(vaultId, appId) {
190
+ const entry = await this.get(vaultId);
191
+ if (!entry) return [];
192
+ const prefix = `${appId}:`;
193
+ return Object.entries(entry.appCredentials).filter(([key]) => key.startsWith(prefix)).map(([, cred]) => cred);
194
+ }
195
+ async getCredential(vaultId, appId, providerId) {
196
+ const entry = await this.get(vaultId);
197
+ if (!entry) return null;
198
+ const key = this.credentialKey(appId, providerId);
199
+ return entry.appCredentials[key] ?? null;
200
+ }
201
+ async getAllCredentials(vaultId, filterByConsent = false) {
202
+ const entry = await this.get(vaultId);
203
+ if (!entry) return [];
204
+ const allCredentials = Object.values(entry.appCredentials);
205
+ if (!filterByConsent || !entry.consent?.enabled) {
206
+ return allCredentials;
207
+ }
208
+ const consentedToolIds = new Set(entry.consent.selectedToolIds);
209
+ return allCredentials.filter((cred) => {
210
+ return Array.from(consentedToolIds).some((toolId) => toolId.startsWith(`${cred.appId}:`));
211
+ });
212
+ }
213
+ async updateCredential(vaultId, appId, providerId, updates) {
214
+ const entry = await this.get(vaultId);
215
+ if (!entry) return;
216
+ const key = this.credentialKey(appId, providerId);
217
+ const credential = entry.appCredentials[key];
218
+ if (!credential) return;
219
+ Object.assign(credential, updates);
220
+ entry.lastAccessAt = Date.now();
221
+ await this.storage.set(this.key(vaultId), entry);
222
+ }
223
+ async shouldStoreCredential(vaultId, appId, toolIds) {
224
+ const entry = await this.get(vaultId);
225
+ if (!entry) return false;
226
+ const consent = entry.consent;
227
+ if (!consent?.enabled) {
228
+ return true;
229
+ }
230
+ if (toolIds && toolIds.length > 0) {
231
+ return toolIds.some((toolId) => consent.selectedToolIds.includes(toolId));
232
+ }
233
+ const consentedToolIds = consent.selectedToolIds;
234
+ return consentedToolIds.some((toolId) => toolId.startsWith(`${appId}:`));
235
+ }
236
+ async invalidateCredential(vaultId, appId, providerId, reason) {
237
+ await this.updateCredential(vaultId, appId, providerId, {
238
+ isValid: false,
239
+ invalidReason: reason
240
+ });
241
+ }
242
+ async refreshOAuthCredential(vaultId, appId, providerId, tokens) {
243
+ const entry = await this.get(vaultId);
244
+ if (!entry) return;
245
+ const key = this.credentialKey(appId, providerId);
246
+ const credential = entry.appCredentials[key];
247
+ if (!credential || credential.credential.type !== "oauth" && credential.credential.type !== "oauth_pkce") return;
248
+ credential.credential.accessToken = tokens.accessToken;
249
+ if (tokens.refreshToken !== void 0) {
250
+ credential.credential.refreshToken = tokens.refreshToken;
251
+ }
252
+ if (tokens.expiresAt !== void 0) {
253
+ credential.credential.expiresAt = tokens.expiresAt;
254
+ credential.expiresAt = tokens.expiresAt;
255
+ }
256
+ credential.isValid = true;
257
+ credential.invalidReason = void 0;
258
+ entry.lastAccessAt = Date.now();
259
+ await this.storage.set(this.key(vaultId), entry);
260
+ }
261
+ // ============================================
262
+ // Cleanup
263
+ // ============================================
264
+ async cleanup() {
265
+ const keys = await this.storage.keys("*");
266
+ const now = Date.now();
267
+ for (const key of keys) {
268
+ const entry = await this.storage.get(key);
269
+ if (!entry) continue;
270
+ const originalLength = entry.pendingAuths.length;
271
+ entry.pendingAuths = entry.pendingAuths.filter((p) => {
272
+ if (now > p.expiresAt && p.status === "pending") {
273
+ p.status = "expired";
274
+ }
275
+ return p.status === "pending";
276
+ });
277
+ if (entry.pendingAuths.length !== originalLength) {
278
+ await this.storage.set(key, entry);
279
+ }
280
+ }
281
+ }
282
+ // ============================================
283
+ // Helpers
284
+ // ============================================
285
+ /**
286
+ * Build the storage key for a vault ID.
287
+ * For non-namespaced storage, includes the namespace prefix.
288
+ */
289
+ key(id) {
290
+ return this.isNamespacedStorage(this.storage.raw) ? id : `${this.namespace}:${id}`;
291
+ }
292
+ /**
293
+ * Type guard to check if storage is a NamespacedStorage.
294
+ */
295
+ isNamespacedStorage(storage) {
296
+ return "namespace" in storage && typeof storage.namespace === "function";
297
+ }
298
+ };
299
+ export {
300
+ StorageAuthorizationVault
301
+ };
@@ -0,0 +1,83 @@
1
+ // libs/auth/src/session/storage/storage-token-store.ts
2
+ import { randomUUID, TypedStorage } from "@frontmcp/utils";
3
+ var StorageTokenStore = class {
4
+ storage;
5
+ namespace;
6
+ defaultTtlSeconds;
7
+ /** Track if original storage was namespaced to avoid double-prefixing */
8
+ storageIsNamespaced;
9
+ constructor(storage, options = {}) {
10
+ this.namespace = options.namespace ?? "tok";
11
+ this.defaultTtlSeconds = options.defaultTtlSeconds;
12
+ this.storageIsNamespaced = "namespace" in storage && typeof storage.namespace === "function";
13
+ const namespacedStorage = this.storageIsNamespaced ? storage.namespace(this.namespace) : storage;
14
+ this.storage = new TypedStorage(namespacedStorage);
15
+ }
16
+ /**
17
+ * Allocate a new unique ID for a token record.
18
+ */
19
+ allocId() {
20
+ return randomUUID();
21
+ }
22
+ /**
23
+ * Store an encrypted token blob.
24
+ *
25
+ * TTL is calculated from blob.exp (epoch seconds) if present.
26
+ * Falls back to defaultTtlSeconds if configured.
27
+ *
28
+ * @param id - Token record ID
29
+ * @param blob - Encrypted token blob
30
+ */
31
+ async put(id, blob) {
32
+ const record = {
33
+ id,
34
+ blob,
35
+ updatedAt: Date.now()
36
+ };
37
+ const ttlSeconds = this.calculateTtl(blob.exp);
38
+ await this.storage.set(this.key(id), record, ttlSeconds ? { ttlSeconds } : void 0);
39
+ }
40
+ /**
41
+ * Retrieve a token record by ID.
42
+ *
43
+ * @param id - Token record ID
44
+ * @returns The secret record, or undefined if not found
45
+ */
46
+ async get(id) {
47
+ const record = await this.storage.get(this.key(id));
48
+ return record ?? void 0;
49
+ }
50
+ /**
51
+ * Delete a token record.
52
+ *
53
+ * @param id - Token record ID
54
+ */
55
+ async del(id) {
56
+ await this.storage.delete(this.key(id));
57
+ }
58
+ /**
59
+ * Calculate TTL in seconds from expiration timestamp.
60
+ *
61
+ * @param exp - Expiration timestamp in epoch seconds
62
+ * @returns TTL in seconds, or undefined if no TTL should be applied
63
+ */
64
+ calculateTtl(exp) {
65
+ if (exp) {
66
+ const nowSeconds = Math.floor(Date.now() / 1e3);
67
+ const ttl = exp - nowSeconds;
68
+ return ttl > 0 ? ttl : 1;
69
+ }
70
+ return this.defaultTtlSeconds;
71
+ }
72
+ /**
73
+ * Build the storage key for a token ID.
74
+ * For namespaced storage, the namespace is handled by the storage layer.
75
+ * For non-namespaced storage, includes the namespace prefix in the key.
76
+ */
77
+ key(id) {
78
+ return this.storageIsNamespaced ? id : `${this.namespace}:${id}`;
79
+ }
80
+ };
81
+ export {
82
+ StorageTokenStore
83
+ };
@@ -0,0 +1,134 @@
1
+ // libs/auth/src/session/storage-authorization.store.ts
2
+ import { TypedStorage } from "@frontmcp/utils";
3
+ import {
4
+ buildCodeRecord,
5
+ buildPendingRecord,
6
+ buildRefreshTokenRecord,
7
+ generateAuthorizationCode,
8
+ generateRefreshTokenValue
9
+ } from "./authorization.store.mjs";
10
+ var StorageAuthorizationStore = class {
11
+ codeStore;
12
+ pendingStore;
13
+ refreshStore;
14
+ namespace;
15
+ storageIsNamespaced;
16
+ constructor(storage, options = {}) {
17
+ this.namespace = options.namespace ?? "oauth";
18
+ this.storageIsNamespaced = this.isNamespacedStorage(storage);
19
+ const base = this.storageIsNamespaced ? storage.namespace(this.namespace) : storage;
20
+ this.codeStore = new TypedStorage(base);
21
+ this.pendingStore = new TypedStorage(base);
22
+ this.refreshStore = new TypedStorage(base);
23
+ }
24
+ // ============================================
25
+ // Record builders (pure; shared with in-memory store)
26
+ // ============================================
27
+ generateCode() {
28
+ return generateAuthorizationCode();
29
+ }
30
+ generateRefreshToken() {
31
+ return generateRefreshTokenValue();
32
+ }
33
+ createCodeRecord(params) {
34
+ return buildCodeRecord(params);
35
+ }
36
+ createPendingRecord(params) {
37
+ return buildPendingRecord(params);
38
+ }
39
+ createRefreshTokenRecord(params) {
40
+ return buildRefreshTokenRecord(params);
41
+ }
42
+ // ============================================
43
+ // Authorization code operations
44
+ // ============================================
45
+ async storeAuthorizationCode(record) {
46
+ await this.codeStore.set(this.key("code", record.code), record, this.ttl(record.expiresAt));
47
+ }
48
+ async getAuthorizationCode(code) {
49
+ const record = await this.codeStore.get(this.key("code", code));
50
+ if (!record) return null;
51
+ if (Date.now() > record.expiresAt) {
52
+ await this.codeStore.delete(this.key("code", code));
53
+ return null;
54
+ }
55
+ return record;
56
+ }
57
+ async markCodeUsed(code, issuedRefreshToken) {
58
+ const record = await this.getAuthorizationCode(code);
59
+ if (record) {
60
+ record.used = true;
61
+ if (issuedRefreshToken) record.issuedRefreshToken = issuedRefreshToken;
62
+ await this.codeStore.set(this.key("code", code), record, this.ttl(record.expiresAt));
63
+ }
64
+ }
65
+ async deleteAuthorizationCode(code) {
66
+ await this.codeStore.delete(this.key("code", code));
67
+ }
68
+ // ============================================
69
+ // Pending authorization operations
70
+ // ============================================
71
+ async storePendingAuthorization(record) {
72
+ await this.pendingStore.set(this.key("pending", record.id), record, this.ttl(record.expiresAt));
73
+ }
74
+ async getPendingAuthorization(id) {
75
+ const record = await this.pendingStore.get(this.key("pending", id));
76
+ if (!record) return null;
77
+ if (Date.now() > record.expiresAt) {
78
+ await this.pendingStore.delete(this.key("pending", id));
79
+ return null;
80
+ }
81
+ return record;
82
+ }
83
+ async deletePendingAuthorization(id) {
84
+ await this.pendingStore.delete(this.key("pending", id));
85
+ }
86
+ // ============================================
87
+ // Refresh token operations
88
+ // ============================================
89
+ async storeRefreshToken(record) {
90
+ await this.refreshStore.set(this.key("refresh", record.token), record, this.ttl(record.expiresAt));
91
+ }
92
+ async getRefreshToken(token) {
93
+ const record = await this.refreshStore.get(this.key("refresh", token));
94
+ if (!record) return null;
95
+ if (Date.now() > record.expiresAt || record.revoked) {
96
+ return null;
97
+ }
98
+ return record;
99
+ }
100
+ async revokeRefreshToken(token) {
101
+ const record = await this.refreshStore.get(this.key("refresh", token));
102
+ if (record) {
103
+ record.revoked = true;
104
+ await this.refreshStore.set(this.key("refresh", token), record, this.ttl(record.expiresAt));
105
+ }
106
+ }
107
+ async rotateRefreshToken(oldToken, newRecord) {
108
+ await this.revokeRefreshToken(oldToken);
109
+ newRecord.previousToken = oldToken;
110
+ await this.storeRefreshToken(newRecord);
111
+ }
112
+ // ============================================
113
+ // Utility
114
+ // ============================================
115
+ async cleanup() {
116
+ }
117
+ // ============================================
118
+ // Internals
119
+ // ============================================
120
+ ttl(expiresAtMs) {
121
+ const ttl = Math.ceil((expiresAtMs - Date.now()) / 1e3);
122
+ return { ttlSeconds: ttl > 0 ? ttl : 1 };
123
+ }
124
+ key(type, id) {
125
+ const composite = `${type}:${id}`;
126
+ return this.storageIsNamespaced ? composite : `${this.namespace}:${composite}`;
127
+ }
128
+ isNamespacedStorage(storage) {
129
+ return "namespace" in storage && typeof storage.namespace === "function";
130
+ }
131
+ };
132
+ export {
133
+ StorageAuthorizationStore
134
+ };
@@ -0,0 +1,33 @@
1
+ // libs/auth/src/session/storage-consent.store.ts
2
+ import { TypedStorage } from "@frontmcp/utils";
3
+ import { consentRecordKey } from "./consent.store.mjs";
4
+ var StorageConsentStore = class {
5
+ storage;
6
+ namespace;
7
+ storageIsNamespaced;
8
+ constructor(storage, options = {}) {
9
+ this.namespace = options.namespace ?? "remembered-consent";
10
+ this.storageIsNamespaced = this.isNamespacedStorage(storage);
11
+ const namespacedStorage = this.storageIsNamespaced ? storage.namespace(this.namespace) : storage;
12
+ this.storage = new TypedStorage(namespacedStorage);
13
+ }
14
+ async get(userSub, clientId) {
15
+ return this.storage.get(this.key(userSub, clientId));
16
+ }
17
+ async set(record) {
18
+ await this.storage.set(this.key(record.userSub, record.clientId), record);
19
+ }
20
+ async delete(userSub, clientId) {
21
+ await this.storage.delete(this.key(userSub, clientId));
22
+ }
23
+ key(userSub, clientId) {
24
+ const base = consentRecordKey(userSub, clientId);
25
+ return this.storageIsNamespaced ? base : `${this.namespace}:${base}`;
26
+ }
27
+ isNamespacedStorage(storage) {
28
+ return "namespace" in storage && typeof storage.namespace === "function";
29
+ }
30
+ };
31
+ export {
32
+ StorageConsentStore
33
+ };
@@ -0,0 +1,55 @@
1
+ // libs/auth/src/session/storage-federated-auth.session.ts
2
+ import { TypedStorage } from "@frontmcp/utils";
3
+ import {
4
+ fromSessionRecord,
5
+ toSessionRecord
6
+ } from "./federated-auth.session.mjs";
7
+ var StorageFederatedAuthSessionStore = class {
8
+ storage;
9
+ namespace;
10
+ storageIsNamespaced;
11
+ constructor(storage, options = {}) {
12
+ this.namespace = options.namespace ?? "fed";
13
+ this.storageIsNamespaced = this.isNamespacedStorage(storage);
14
+ const namespacedStorage = this.storageIsNamespaced ? storage.namespace(this.namespace) : storage;
15
+ this.storage = new TypedStorage(namespacedStorage);
16
+ }
17
+ async store(session) {
18
+ await this.put(session);
19
+ }
20
+ async update(session) {
21
+ await this.put(session);
22
+ }
23
+ async get(id) {
24
+ const record = await this.storage.get(this.key(id));
25
+ if (!record) {
26
+ return null;
27
+ }
28
+ if (Date.now() > record.expiresAt) {
29
+ await this.storage.delete(this.key(id));
30
+ return null;
31
+ }
32
+ return fromSessionRecord(record);
33
+ }
34
+ async delete(id) {
35
+ await this.storage.delete(this.key(id));
36
+ }
37
+ async put(session) {
38
+ const record = toSessionRecord(session);
39
+ const ttlSeconds = this.calculateTtl(record.expiresAt);
40
+ await this.storage.set(this.key(session.id), record, ttlSeconds ? { ttlSeconds } : void 0);
41
+ }
42
+ calculateTtl(expiresAtMs) {
43
+ const ttl = Math.ceil((expiresAtMs - Date.now()) / 1e3);
44
+ return ttl > 0 ? ttl : 1;
45
+ }
46
+ key(id) {
47
+ return this.storageIsNamespaced ? id : `${this.namespace}:${id}`;
48
+ }
49
+ isNamespacedStorage(storage) {
50
+ return "namespace" in storage && typeof storage.namespace === "function";
51
+ }
52
+ };
53
+ export {
54
+ StorageFederatedAuthSessionStore
55
+ };