@frontmcp/auth 1.10.16 → 1.11.0-rc.2

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,140 @@
1
+ // libs/auth/src/vault/auth-providers.registry.ts
2
+ import { CredentialProviderAlreadyRegisteredError } from "../errors/auth-internal.errors.mjs";
3
+ var AuthProvidersRegistry = class {
4
+ providers = /* @__PURE__ */ new Map();
5
+ defaultCacheTtl;
6
+ constructor(options) {
7
+ this.defaultCacheTtl = options?.defaultCacheTtl ?? 36e5;
8
+ if (options?.providers) {
9
+ for (const provider of options.providers) {
10
+ this.register(provider);
11
+ }
12
+ }
13
+ }
14
+ /**
15
+ * Register a credential provider
16
+ *
17
+ * @param config - Provider configuration
18
+ * @throws CredentialProviderAlreadyRegisteredError if provider with same name already registered
19
+ */
20
+ register(config) {
21
+ if (this.providers.has(config.name)) {
22
+ throw new CredentialProviderAlreadyRegisteredError(config.name);
23
+ }
24
+ const normalized = this.normalize(config);
25
+ this.providers.set(config.name, normalized);
26
+ }
27
+ /**
28
+ * Unregister a credential provider
29
+ *
30
+ * @param name - Provider name to unregister
31
+ * @returns true if provider was unregistered, false if not found
32
+ */
33
+ unregister(name) {
34
+ return this.providers.delete(name);
35
+ }
36
+ /**
37
+ * Get a provider configuration by name
38
+ *
39
+ * @param name - Provider name
40
+ * @returns Provider config or undefined if not found
41
+ */
42
+ get(name) {
43
+ return this.providers.get(name);
44
+ }
45
+ /**
46
+ * Check if a provider is registered
47
+ *
48
+ * @param name - Provider name
49
+ * @returns true if provider is registered
50
+ */
51
+ has(name) {
52
+ return this.providers.has(name);
53
+ }
54
+ /**
55
+ * Get all registered provider names
56
+ */
57
+ getNames() {
58
+ return [...this.providers.keys()];
59
+ }
60
+ /**
61
+ * Get all provider configurations
62
+ */
63
+ getAll() {
64
+ return [...this.providers.values()];
65
+ }
66
+ /**
67
+ * Get providers by scope
68
+ *
69
+ * @param scope - Credential scope to filter by
70
+ */
71
+ getByScope(scope) {
72
+ return this.getAll().filter((p) => p.scope === scope);
73
+ }
74
+ /**
75
+ * Get providers by loading strategy
76
+ *
77
+ * @param loading - Loading strategy to filter by
78
+ */
79
+ getByLoading(loading) {
80
+ return this.getAll().filter((p) => p.loading === loading);
81
+ }
82
+ /**
83
+ * Get providers that are required
84
+ */
85
+ getRequired() {
86
+ return this.getAll().filter((p) => p.required);
87
+ }
88
+ /**
89
+ * Get providers that should be eagerly loaded
90
+ */
91
+ getEager() {
92
+ return this.getByLoading("eager");
93
+ }
94
+ /**
95
+ * Get providers that should be lazily loaded
96
+ */
97
+ getLazy() {
98
+ return this.getByLoading("lazy");
99
+ }
100
+ /**
101
+ * Get the number of registered providers
102
+ */
103
+ get size() {
104
+ return this.providers.size;
105
+ }
106
+ /**
107
+ * Check if registry is empty
108
+ */
109
+ isEmpty() {
110
+ return this.providers.size === 0;
111
+ }
112
+ /**
113
+ * Clear all registered providers
114
+ */
115
+ clear() {
116
+ this.providers.clear();
117
+ }
118
+ /**
119
+ * Normalize provider config with defaults
120
+ */
121
+ normalize(config) {
122
+ return {
123
+ name: config.name,
124
+ description: config.description,
125
+ scope: config.scope,
126
+ loading: config.loading,
127
+ cacheTtl: config.cacheTtl ?? this.defaultCacheTtl,
128
+ factory: config.factory,
129
+ refresh: config.refresh,
130
+ toHeaders: config.toHeaders,
131
+ metadata: config.metadata,
132
+ required: config.required ?? false
133
+ };
134
+ }
135
+ };
136
+ var AUTH_PROVIDERS_REGISTRY = /* @__PURE__ */ Symbol.for("frontmcp:AUTH_PROVIDERS_REGISTRY");
137
+ export {
138
+ AUTH_PROVIDERS_REGISTRY,
139
+ AuthProvidersRegistry
140
+ };
@@ -0,0 +1,47 @@
1
+ // libs/auth/src/vault/auth-providers.types.ts
2
+ import { internalZ as z } from "@frontmcp/lazy-zod";
3
+ var credentialScopeSchema = z.enum(["global", "user", "session"]);
4
+ var loadingStrategySchema = z.enum(["eager", "lazy"]);
5
+ var getCredentialOptionsSchema = z.object({
6
+ forceRefresh: z.boolean().optional(),
7
+ scopes: z.array(z.string()).optional(),
8
+ timeout: z.number().positive().optional()
9
+ }).strict();
10
+ var credentialProviderConfigSchema = z.object({
11
+ name: z.string().min(1),
12
+ description: z.string().optional(),
13
+ scope: credentialScopeSchema,
14
+ loading: loadingStrategySchema,
15
+ cacheTtl: z.number().nonnegative().optional(),
16
+ // Functions validated at runtime, not via Zod (Zod 4 compatibility)
17
+ factory: z.any(),
18
+ refresh: z.any().optional(),
19
+ toHeaders: z.any().optional(),
20
+ metadata: z.record(z.string(), z.unknown()).optional(),
21
+ required: z.boolean().optional()
22
+ }).strict();
23
+ var authProviderMappingSchema = z.union([
24
+ z.string(),
25
+ z.object({
26
+ name: z.string().min(1),
27
+ required: z.boolean().optional().default(true),
28
+ scopes: z.array(z.string()).optional(),
29
+ alias: z.string().optional()
30
+ }).strict()
31
+ ]);
32
+ var authProvidersVaultOptionsSchema = z.object({
33
+ enabled: z.boolean().optional(),
34
+ useSharedStorage: z.boolean().optional().default(true),
35
+ namespace: z.string().optional().default("authproviders:"),
36
+ defaultCacheTtl: z.number().nonnegative().optional().default(36e5),
37
+ maxCredentialsPerSession: z.number().positive().optional().default(100),
38
+ providers: z.array(credentialProviderConfigSchema).optional()
39
+ }).strict();
40
+ export {
41
+ authProviderMappingSchema,
42
+ authProvidersVaultOptionsSchema,
43
+ credentialProviderConfigSchema,
44
+ credentialScopeSchema,
45
+ getCredentialOptionsSchema,
46
+ loadingStrategySchema
47
+ };
@@ -0,0 +1,206 @@
1
+ // libs/auth/src/vault/auth-providers.vault.ts
2
+ import { extractCredentialExpiry } from "./credential-helpers.mjs";
3
+ import { AuthInvalidInputError, CredentialStorageError } from "../errors/auth-internal.errors.mjs";
4
+ var AuthProvidersVault = class {
5
+ constructor(baseVault, namespace = "authproviders:", logger) {
6
+ this.baseVault = baseVault;
7
+ this.namespace = namespace;
8
+ this.logger = logger;
9
+ }
10
+ baseVault;
11
+ namespace;
12
+ logger;
13
+ /**
14
+ * Store a credential in the vault
15
+ *
16
+ * @param sessionId - Current session ID
17
+ * @param providerId - Provider name
18
+ * @param credential - Credential to store
19
+ * @param scope - Credential scope
20
+ * @param userId - User ID (required for user scope)
21
+ */
22
+ async storeCredential(sessionId, providerId, credential, scope, userId) {
23
+ const vaultKey = this.buildVaultKey(sessionId, scope, userId);
24
+ const appCredential = {
25
+ appId: this.namespace,
26
+ providerId,
27
+ credential,
28
+ acquiredAt: Date.now(),
29
+ isValid: true,
30
+ expiresAt: extractCredentialExpiry(credential)
31
+ };
32
+ try {
33
+ await this.baseVault.addAppCredential(vaultKey, appCredential);
34
+ this.logger?.debug(`Stored credential "${providerId}" with scope "${scope}"`);
35
+ } catch (error) {
36
+ this.logger?.warn(`Failed to store credential "${providerId}":`, error);
37
+ throw error;
38
+ }
39
+ }
40
+ /**
41
+ * Get a credential from the vault
42
+ *
43
+ * @param sessionId - Current session ID
44
+ * @param providerId - Provider name
45
+ * @param scope - Credential scope
46
+ * @param userId - User ID (required for user scope)
47
+ * @returns Credential or null if not found
48
+ */
49
+ async getCredential(sessionId, providerId, scope, userId) {
50
+ const vaultKey = this.buildVaultKey(sessionId, scope, userId);
51
+ try {
52
+ const appCredential = await this.baseVault.getCredential(vaultKey, this.namespace, providerId);
53
+ if (!appCredential) {
54
+ return null;
55
+ }
56
+ if (!appCredential.isValid) {
57
+ this.logger?.debug(`Credential "${providerId}" is marked as invalid`);
58
+ return null;
59
+ }
60
+ if (appCredential.expiresAt && Date.now() >= appCredential.expiresAt) {
61
+ this.logger?.debug(`Credential "${providerId}" has expired`);
62
+ return null;
63
+ }
64
+ return appCredential.credential;
65
+ } catch (error) {
66
+ this.logger?.warn(`Failed to get credential "${providerId}":`, error);
67
+ if (error instanceof CredentialStorageError) {
68
+ throw error;
69
+ }
70
+ throw new CredentialStorageError(
71
+ `Storage error while retrieving credential "${providerId}": ${error instanceof Error ? error.message : String(error)}`
72
+ );
73
+ }
74
+ }
75
+ /**
76
+ * Remove a credential from the vault
77
+ *
78
+ * @param sessionId - Current session ID
79
+ * @param providerId - Provider name
80
+ * @param scope - Credential scope
81
+ * @param userId - User ID (required for user scope)
82
+ */
83
+ async removeCredential(sessionId, providerId, scope, userId) {
84
+ const vaultKey = this.buildVaultKey(sessionId, scope, userId);
85
+ try {
86
+ await this.baseVault.removeAppCredential(vaultKey, this.namespace, providerId);
87
+ this.logger?.debug(`Removed credential "${providerId}"`);
88
+ } catch (error) {
89
+ this.logger?.warn(`Failed to remove credential "${providerId}":`, error);
90
+ }
91
+ }
92
+ /**
93
+ * Invalidate a credential (mark as invalid without removing)
94
+ *
95
+ * @param sessionId - Current session ID
96
+ * @param providerId - Provider name
97
+ * @param scope - Credential scope
98
+ * @param reason - Reason for invalidation
99
+ * @param userId - User ID (required for user scope)
100
+ */
101
+ async invalidateCredential(sessionId, providerId, scope, reason, userId) {
102
+ const vaultKey = this.buildVaultKey(sessionId, scope, userId);
103
+ try {
104
+ await this.baseVault.invalidateCredential(vaultKey, this.namespace, providerId, reason);
105
+ this.logger?.debug(`Invalidated credential "${providerId}": ${reason}`);
106
+ } catch (error) {
107
+ this.logger?.warn(`Failed to invalidate credential "${providerId}":`, error);
108
+ }
109
+ }
110
+ /**
111
+ * Update OAuth credential tokens (for refresh)
112
+ *
113
+ * @param sessionId - Current session ID
114
+ * @param providerId - Provider name
115
+ * @param scope - Credential scope
116
+ * @param tokens - New tokens
117
+ * @param userId - User ID (required for user scope)
118
+ */
119
+ async refreshOAuthCredential(sessionId, providerId, scope, tokens, userId) {
120
+ const vaultKey = this.buildVaultKey(sessionId, scope, userId);
121
+ try {
122
+ await this.baseVault.refreshOAuthCredential(vaultKey, this.namespace, providerId, tokens);
123
+ this.logger?.debug(`Refreshed OAuth credential "${providerId}"`);
124
+ } catch (error) {
125
+ this.logger?.warn(`Failed to refresh OAuth credential "${providerId}":`, error);
126
+ throw error;
127
+ }
128
+ }
129
+ /**
130
+ * Get all credentials for a session
131
+ *
132
+ * @param sessionId - Current session ID
133
+ * @param scope - Optional scope filter
134
+ * @param userId - User ID (required for user scope)
135
+ */
136
+ async getAllCredentials(sessionId, scope, userId) {
137
+ if (scope) {
138
+ const vaultKey = this.buildVaultKey(sessionId, scope, userId);
139
+ try {
140
+ return await this.baseVault.getAppCredentials(vaultKey, this.namespace);
141
+ } catch {
142
+ return [];
143
+ }
144
+ }
145
+ const credentials = [];
146
+ try {
147
+ const sessionCreds = await this.baseVault.getAppCredentials(
148
+ this.buildVaultKey(sessionId, "session"),
149
+ this.namespace
150
+ );
151
+ credentials.push(...sessionCreds);
152
+ } catch {
153
+ }
154
+ if (userId) {
155
+ try {
156
+ const userCreds = await this.baseVault.getAppCredentials(
157
+ this.buildVaultKey(sessionId, "user", userId),
158
+ this.namespace
159
+ );
160
+ credentials.push(...userCreds);
161
+ } catch {
162
+ }
163
+ }
164
+ try {
165
+ const globalCreds = await this.baseVault.getAppCredentials(
166
+ this.buildVaultKey(sessionId, "global"),
167
+ this.namespace
168
+ );
169
+ credentials.push(...globalCreds);
170
+ } catch {
171
+ }
172
+ return credentials;
173
+ }
174
+ /**
175
+ * Build vault key based on scope
176
+ *
177
+ * Key patterns:
178
+ * - global: `authproviders:global`
179
+ * - user: `authproviders:user:{userId}`
180
+ * - session: `authproviders:session:{sessionId}`
181
+ */
182
+ buildVaultKey(sessionId, scope, userId) {
183
+ switch (scope) {
184
+ case "global":
185
+ return `${this.namespace}global`;
186
+ case "user":
187
+ if (!userId) {
188
+ throw new AuthInvalidInputError(
189
+ `userId is required for user-scoped credentials (namespace: ${this.namespace})`
190
+ );
191
+ }
192
+ return `${this.namespace}user:${userId}`;
193
+ case "session":
194
+ return `${this.namespace}session:${sessionId}`;
195
+ default: {
196
+ const _exhaustive = scope;
197
+ throw new AuthInvalidInputError(`Unknown credential scope: ${_exhaustive}`);
198
+ }
199
+ }
200
+ }
201
+ };
202
+ var AUTH_PROVIDERS_VAULT = /* @__PURE__ */ Symbol.for("frontmcp:AUTH_PROVIDERS_VAULT");
203
+ export {
204
+ AUTH_PROVIDERS_VAULT,
205
+ AuthProvidersVault
206
+ };
@@ -0,0 +1,187 @@
1
+ // libs/auth/src/vault/credential-cache.ts
2
+ var CredentialCache = class {
3
+ cache = /* @__PURE__ */ new Map();
4
+ maxSize;
5
+ stats = { hits: 0, misses: 0, evictions: 0, size: 0 };
6
+ constructor(maxSize = 100) {
7
+ this.maxSize = maxSize;
8
+ }
9
+ /**
10
+ * Get a cached credential
11
+ *
12
+ * @param providerId - Provider name
13
+ * @returns Resolved credential or undefined if not cached or expired
14
+ */
15
+ get(providerId) {
16
+ const entry = this.cache.get(providerId);
17
+ if (!entry) {
18
+ this.stats.misses++;
19
+ return void 0;
20
+ }
21
+ if (this.isExpired(entry)) {
22
+ this.cache.delete(providerId);
23
+ this.stats.size = this.cache.size;
24
+ this.stats.misses++;
25
+ this.stats.evictions++;
26
+ return void 0;
27
+ }
28
+ if (entry.resolved.expiresAt && Date.now() >= entry.resolved.expiresAt) {
29
+ this.cache.delete(providerId);
30
+ this.stats.size = this.cache.size;
31
+ this.stats.misses++;
32
+ this.stats.evictions++;
33
+ return void 0;
34
+ }
35
+ this.stats.hits++;
36
+ return entry.resolved;
37
+ }
38
+ /**
39
+ * Cache a resolved credential
40
+ *
41
+ * @param providerId - Provider name
42
+ * @param resolved - Resolved credential to cache
43
+ * @param ttl - TTL in milliseconds (0 = no TTL, rely on credential expiry)
44
+ */
45
+ set(providerId, resolved, ttl = 0) {
46
+ if (this.cache.size >= this.maxSize && !this.cache.has(providerId)) {
47
+ this.evictOldest();
48
+ }
49
+ const entry = {
50
+ resolved,
51
+ cachedAt: Date.now(),
52
+ ttl
53
+ };
54
+ this.cache.set(providerId, entry);
55
+ this.stats.size = this.cache.size;
56
+ }
57
+ /**
58
+ * Check if a credential is cached and valid
59
+ *
60
+ * @param providerId - Provider name
61
+ * @returns true if cached and not expired
62
+ */
63
+ has(providerId) {
64
+ const entry = this.cache.get(providerId);
65
+ if (!entry) return false;
66
+ if (this.isExpired(entry)) {
67
+ this.cache.delete(providerId);
68
+ this.stats.size = this.cache.size;
69
+ this.stats.evictions++;
70
+ return false;
71
+ }
72
+ return true;
73
+ }
74
+ /**
75
+ * Invalidate (remove) a cached credential
76
+ *
77
+ * @param providerId - Provider name to invalidate
78
+ * @returns true if credential was removed
79
+ */
80
+ invalidate(providerId) {
81
+ const deleted = this.cache.delete(providerId);
82
+ if (deleted) {
83
+ this.stats.size = this.cache.size;
84
+ }
85
+ return deleted;
86
+ }
87
+ /**
88
+ * Invalidate all cached credentials
89
+ */
90
+ invalidateAll() {
91
+ this.cache.clear();
92
+ this.stats.size = 0;
93
+ }
94
+ /**
95
+ * Invalidate credentials by scope
96
+ *
97
+ * @param scope - Credential scope to invalidate
98
+ */
99
+ invalidateByScope(scope) {
100
+ for (const [key, entry] of this.cache) {
101
+ if (entry.resolved.scope === scope) {
102
+ this.cache.delete(key);
103
+ }
104
+ }
105
+ this.stats.size = this.cache.size;
106
+ }
107
+ /**
108
+ * Get all cached provider IDs
109
+ */
110
+ keys() {
111
+ this.cleanup();
112
+ return [...this.cache.keys()];
113
+ }
114
+ /**
115
+ * Get cache size
116
+ */
117
+ get size() {
118
+ return this.cache.size;
119
+ }
120
+ /**
121
+ * Get cache statistics
122
+ */
123
+ getStats() {
124
+ return { ...this.stats };
125
+ }
126
+ /**
127
+ * Reset cache statistics
128
+ */
129
+ resetStats() {
130
+ this.stats = { hits: 0, misses: 0, evictions: 0, size: this.cache.size };
131
+ }
132
+ /**
133
+ * Clean up expired entries
134
+ */
135
+ cleanup() {
136
+ const now = Date.now();
137
+ for (const [key, entry] of this.cache) {
138
+ if (this.isExpiredAt(entry, now)) {
139
+ this.cache.delete(key);
140
+ this.stats.evictions++;
141
+ }
142
+ }
143
+ this.stats.size = this.cache.size;
144
+ }
145
+ /**
146
+ * Check if entry is expired based on TTL
147
+ */
148
+ isExpired(entry) {
149
+ return this.isExpiredAt(entry, Date.now());
150
+ }
151
+ /**
152
+ * Check if entry is expired at a given timestamp
153
+ */
154
+ isExpiredAt(entry, now) {
155
+ if (entry.ttl > 0 && now - entry.cachedAt >= entry.ttl) {
156
+ return true;
157
+ }
158
+ if (entry.resolved.expiresAt && now >= entry.resolved.expiresAt) {
159
+ return true;
160
+ }
161
+ if (!entry.resolved.isValid) {
162
+ return true;
163
+ }
164
+ return false;
165
+ }
166
+ /**
167
+ * Evict the oldest entry from cache
168
+ */
169
+ evictOldest() {
170
+ let oldestKey;
171
+ let oldestTime = Infinity;
172
+ for (const [key, entry] of this.cache) {
173
+ if (entry.cachedAt < oldestTime) {
174
+ oldestTime = entry.cachedAt;
175
+ oldestKey = key;
176
+ }
177
+ }
178
+ if (oldestKey) {
179
+ this.cache.delete(oldestKey);
180
+ this.stats.evictions++;
181
+ this.stats.size = this.cache.size;
182
+ }
183
+ }
184
+ };
185
+ export {
186
+ CredentialCache
187
+ };
@@ -0,0 +1,15 @@
1
+ // libs/auth/src/vault/credential-helpers.ts
2
+ function extractCredentialExpiry(credential) {
3
+ switch (credential.type) {
4
+ case "oauth":
5
+ case "oauth_pkce":
6
+ case "bearer":
7
+ case "service_account":
8
+ return credential.expiresAt;
9
+ default:
10
+ return void 0;
11
+ }
12
+ }
13
+ export {
14
+ extractCredentialExpiry
15
+ };
@@ -0,0 +1,91 @@
1
+ // libs/auth/src/vault/credential-loaders/eager-loader.ts
2
+ import { extractCredentialExpiry } from "../credential-helpers.mjs";
3
+ var EagerCredentialLoader = class {
4
+ constructor(registry, cache, logger) {
5
+ this.registry = registry;
6
+ this.cache = cache;
7
+ this.logger = logger;
8
+ }
9
+ registry;
10
+ cache;
11
+ logger;
12
+ /**
13
+ * Load all eager credentials for a session.
14
+ * Called during session initialization.
15
+ *
16
+ * @param context - Factory context with session/user info
17
+ * @returns Map of provider name to resolved credential
18
+ */
19
+ async loadForSession(context) {
20
+ const startTime = Date.now();
21
+ const eagerProviders = this.registry.getEager();
22
+ if (eagerProviders.length === 0) {
23
+ return {
24
+ loaded: /* @__PURE__ */ new Map(),
25
+ failed: /* @__PURE__ */ new Map(),
26
+ duration: Date.now() - startTime
27
+ };
28
+ }
29
+ this.logger?.debug(`Loading ${eagerProviders.length} eager credential providers`);
30
+ const loaded = /* @__PURE__ */ new Map();
31
+ const failed = /* @__PURE__ */ new Map();
32
+ const results = await Promise.allSettled(
33
+ eagerProviders.map(async (config) => {
34
+ const credential = await this.loadOne(config, context);
35
+ return { name: config.name, credential, config };
36
+ })
37
+ );
38
+ for (const result of results) {
39
+ if (result.status === "fulfilled") {
40
+ const { name, credential, config } = result.value;
41
+ if (credential) {
42
+ const resolved = this.wrapCredential(name, credential, config);
43
+ loaded.set(name, resolved);
44
+ this.cache.set(name, resolved, config.cacheTtl);
45
+ this.logger?.debug(`Loaded eager credential: ${name}`);
46
+ } else if (config.required) {
47
+ failed.set(name, new Error(`Required credential "${name}" returned null`));
48
+ this.logger?.warn(`Required eager credential "${name}" returned null`);
49
+ }
50
+ } else {
51
+ const providerName = result.reason?.providerName || "unknown";
52
+ const error = result.reason instanceof Error ? result.reason : new Error(String(result.reason));
53
+ failed.set(providerName, error);
54
+ this.logger?.warn(`Failed to load eager credential "${providerName}":`, error.message);
55
+ }
56
+ }
57
+ const duration = Date.now() - startTime;
58
+ this.logger?.debug(`Eager loading complete: ${loaded.size} loaded, ${failed.size} failed, ${duration}ms`);
59
+ return { loaded, failed, duration };
60
+ }
61
+ /**
62
+ * Load a single credential
63
+ */
64
+ async loadOne(config, context) {
65
+ try {
66
+ return await config.factory({
67
+ ...context,
68
+ metadata: config.metadata
69
+ });
70
+ } catch (error) {
71
+ error.providerName = config.name;
72
+ throw error;
73
+ }
74
+ }
75
+ /**
76
+ * Wrap credential with resolved metadata
77
+ */
78
+ wrapCredential(providerId, credential, config) {
79
+ return {
80
+ credential,
81
+ providerId,
82
+ acquiredAt: Date.now(),
83
+ expiresAt: extractCredentialExpiry(credential),
84
+ isValid: true,
85
+ scope: config.scope
86
+ };
87
+ }
88
+ };
89
+ export {
90
+ EagerCredentialLoader
91
+ };