@frontmcp/auth 1.11.2 → 1.12.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 (128) hide show
  1. package/README.md +2 -2
  2. package/authorities/authorities.context.d.ts.map +1 -1
  3. package/authorities/authorities.engine.d.ts +1 -1
  4. package/authorities/authorities.engine.d.ts.map +1 -1
  5. package/authorities/authorities.evaluator.d.ts.map +1 -1
  6. package/authorities/authorities.metadata-augment.d.ts +3 -0
  7. package/authorities/authorities.metadata-augment.d.ts.map +1 -1
  8. package/authorities/authorities.profiles.d.ts +1 -1
  9. package/authorities/authorities.profiles.d.ts.map +1 -1
  10. package/authorities/authorities.registry.d.ts +1 -1
  11. package/authorities/authorities.registry.d.ts.map +1 -1
  12. package/authorities/authorities.scope-mapping.d.ts +1 -1
  13. package/authorities/authorities.scope-mapping.d.ts.map +1 -1
  14. package/authorities/authorities.types.d.ts +4 -0
  15. package/authorities/authorities.types.d.ts.map +1 -1
  16. package/authorization/orchestrated.accessor.d.ts.map +1 -1
  17. package/authorization/public.authorization.d.ts.map +1 -1
  18. package/authorization/transparent.authorization.d.ts +1 -1
  19. package/authorization/transparent.authorization.d.ts.map +1 -1
  20. package/browser/authorities/authorities.context.mjs +3 -1
  21. package/browser/authorities/authorities.engine.mjs +32 -33
  22. package/browser/authorities/authorities.evaluator.mjs +42 -16
  23. package/browser/authorization/dcr-client.registry.mjs +1 -1
  24. package/browser/authorization/public.authorization.mjs +1 -1
  25. package/browser/authorization/transparent.authorization.mjs +1 -1
  26. package/browser/cimd/cimd-client-assertion.mjs +108 -0
  27. package/browser/cimd/cimd-redis.cache.mjs +5 -3
  28. package/browser/cimd/cimd.errors.mjs +11 -0
  29. package/browser/cimd/cimd.service.mjs +127 -31
  30. package/browser/cimd/cimd.types.mjs +15 -10
  31. package/browser/cimd/index.mjs +13 -1
  32. package/browser/errors/auth-internal.error.mjs +3 -2
  33. package/browser/errors/auth-internal.errors.mjs +8 -1
  34. package/browser/errors/index.mjs +2 -0
  35. package/browser/index.mjs +21 -1
  36. package/browser/jwks/index.mjs +2 -0
  37. package/browser/jwks/jwks.service.mjs +22 -103
  38. package/browser/jwks/local-token-keys.mjs +155 -0
  39. package/browser/options/public.schema.mjs +7 -4
  40. package/browser/options/shared.schemas.mjs +38 -13
  41. package/browser/session/federated-auth.session.mjs +2 -0
  42. package/browser/session/incremental-auth-ticket.mjs +24 -1
  43. package/browser/session/index.mjs +4 -0
  44. package/browser/session/redis-session.store.mjs +3 -3
  45. package/browser/session/session-crypto.mjs +4 -4
  46. package/browser/session/storage/storage-authorization-vault.mjs +3 -1
  47. package/browser/session/token.vault.mjs +1 -1
  48. package/browser/session/utils/index.mjs +9 -1
  49. package/browser/session/utils/session-crypto.utils.mjs +4 -0
  50. package/browser/ui/index.mjs +2 -0
  51. package/browser/ui/templates.mjs +65 -11
  52. package/browser/upstream/index.mjs +6 -0
  53. package/browser/upstream/upstream-client-registration.mjs +273 -0
  54. package/cimd/cimd-client-assertion.d.ts +43 -0
  55. package/cimd/cimd-client-assertion.d.ts.map +1 -0
  56. package/cimd/cimd-redis.cache.d.ts +2 -2
  57. package/cimd/cimd-redis.cache.d.ts.map +1 -1
  58. package/cimd/cimd.cache.d.ts +1 -1
  59. package/cimd/cimd.cache.d.ts.map +1 -1
  60. package/cimd/cimd.errors.d.ts +14 -0
  61. package/cimd/cimd.errors.d.ts.map +1 -1
  62. package/cimd/cimd.service.d.ts +37 -2
  63. package/cimd/cimd.service.d.ts.map +1 -1
  64. package/cimd/cimd.types.d.ts +6 -0
  65. package/cimd/cimd.types.d.ts.map +1 -1
  66. package/cimd/index.d.ts +3 -2
  67. package/cimd/index.d.ts.map +1 -1
  68. package/errors/auth-internal.error.d.ts +4 -5
  69. package/errors/auth-internal.error.d.ts.map +1 -1
  70. package/errors/auth-internal.errors.d.ts +8 -0
  71. package/errors/auth-internal.errors.d.ts.map +1 -1
  72. package/errors/index.d.ts +1 -1
  73. package/errors/index.d.ts.map +1 -1
  74. package/esm/index.mjs +54 -14
  75. package/esm/package.json +5 -5
  76. package/index.d.ts +12 -10
  77. package/index.d.ts.map +1 -1
  78. package/index.js +58 -18
  79. package/jwks/index.d.ts +2 -0
  80. package/jwks/index.d.ts.map +1 -1
  81. package/jwks/jwks.service.d.ts +12 -12
  82. package/jwks/jwks.service.d.ts.map +1 -1
  83. package/jwks/jwks.types.d.ts +2 -0
  84. package/jwks/jwks.types.d.ts.map +1 -1
  85. package/jwks/local-token-keys.d.ts +41 -0
  86. package/jwks/local-token-keys.d.ts.map +1 -0
  87. package/options/interfaces.d.ts +64 -15
  88. package/options/interfaces.d.ts.map +1 -1
  89. package/options/public.schema.d.ts.map +1 -1
  90. package/options/shared.schemas.d.ts.map +1 -1
  91. package/package.json +5 -5
  92. package/session/authorization.store.d.ts +9 -2
  93. package/session/authorization.store.d.ts.map +1 -1
  94. package/session/federated-auth.session.d.ts +8 -0
  95. package/session/federated-auth.session.d.ts.map +1 -1
  96. package/session/incremental-auth-ticket.d.ts +10 -0
  97. package/session/incremental-auth-ticket.d.ts.map +1 -1
  98. package/session/index.d.ts +2 -2
  99. package/session/index.d.ts.map +1 -1
  100. package/session/redis-session.store.d.ts +1 -1
  101. package/session/redis-session.store.d.ts.map +1 -1
  102. package/session/session-crypto.d.ts.map +1 -1
  103. package/session/storage/in-memory-authorization-vault.d.ts.map +1 -1
  104. package/session/storage/storage-authorization-vault.d.ts +2 -2
  105. package/session/storage/storage-authorization-vault.d.ts.map +1 -1
  106. package/session/storage/storage-token-store.d.ts +2 -2
  107. package/session/storage/storage-token-store.d.ts.map +1 -1
  108. package/session/token.refresh.d.ts.map +1 -1
  109. package/session/utils/index.d.ts +1 -1
  110. package/session/utils/index.d.ts.map +1 -1
  111. package/session/utils/session-crypto.utils.d.ts +5 -0
  112. package/session/utils/session-crypto.utils.d.ts.map +1 -1
  113. package/ui/index.d.ts +2 -2
  114. package/ui/index.d.ts.map +1 -1
  115. package/ui/templates.d.ts +33 -0
  116. package/ui/templates.d.ts.map +1 -1
  117. package/upstream/index.d.ts +3 -0
  118. package/upstream/index.d.ts.map +1 -0
  119. package/upstream/upstream-client-registration.d.ts +50 -0
  120. package/upstream/upstream-client-registration.d.ts.map +1 -0
  121. package/vault/auth-providers.accessor.d.ts +1 -1
  122. package/vault/auth-providers.accessor.d.ts.map +1 -1
  123. package/vault/auth-providers.registry.d.ts +1 -1
  124. package/vault/auth-providers.registry.d.ts.map +1 -1
  125. package/vault/credential-cache.d.ts +1 -1
  126. package/vault/credential-cache.d.ts.map +1 -1
  127. package/vault/credential-loaders/eager-loader.d.ts +2 -2
  128. package/vault/credential-loaders/eager-loader.d.ts.map +1 -1
@@ -117,14 +117,14 @@ function buildIncrementalAuthPage(params) {
117
117
  return centeredCardLayout(content, { title: `Authorize ${app.appName}` });
118
118
  }
119
119
  function buildFederatedLoginPage(params) {
120
- const { providers, clientName, pendingAuthId, callbackPath } = params;
120
+ const { providers, clientName, pendingAuthId, callbackPath, allowSkip = true } = params;
121
121
  const providerCards = providers.map((provider) => {
122
122
  const isPrimaryBadge = provider.isPrimary ? `<span class="px-2 py-0.5 text-xs font-medium bg-blue-600 text-white rounded-full">Primary</span>` : "";
123
123
  const providerUrl = provider.providerUrl ? `<p class="text-xs text-gray-400 font-mono truncate">${escapeHtml(provider.providerUrl)}</p>` : "";
124
124
  const appIds = provider.appIds.length > 0 ? `<p class="text-xs text-gray-500 mt-1">Apps: ${provider.appIds.map((id) => escapeHtml(id)).join(", ")}</p>` : "";
125
125
  return `<label class="flex items-start gap-4 p-4 bg-white border-2 border-gray-200 rounded-xl cursor-pointer hover:border-blue-300 transition-colors has-[:checked]:border-blue-500 has-[:checked]:bg-blue-50">
126
126
  <input type="checkbox" name="providers" value="${escapeHtml(provider.providerId)}"
127
- class="mt-1 w-5 h-5 rounded border-gray-300" ${provider.isPrimary ? "checked" : ""}>
127
+ class="mt-1 w-5 h-5 rounded border-gray-300" ${provider.isPrimary || provider.required ? "checked" : ""}${provider.required ? " required" : ""}>
128
128
  <div class="flex-1">
129
129
  <div class="flex items-center gap-2 mb-1">
130
130
  <span class="font-semibold text-gray-900">${escapeHtml(provider.providerName)}</span>
@@ -136,6 +136,14 @@ function buildFederatedLoginPage(params) {
136
136
  </div>
137
137
  </label>`;
138
138
  }).join("\n");
139
+ const skipAllButton = allowSkip ? `<button type="button"
140
+ class="flex-1 px-6 py-3 text-gray-700 bg-gray-100 hover:bg-gray-200 font-medium rounded-lg transition-colors"
141
+ onclick="document.querySelectorAll('input[name=providers]').forEach(cb => cb.checked = false); document.getElementById('federated-form').submit();">
142
+ Skip All
143
+ </button>` : "";
144
+ const skipNote = allowSkip ? `<div class="mt-6 p-4 bg-blue-50 border border-blue-200 rounded-lg text-sm text-blue-800">
145
+ Skipped providers can be authorized later when you try to use their tools (progressive authorization).
146
+ </div>` : "";
139
147
  const content = `
140
148
  <h1 class="text-3xl font-bold text-gray-900 mb-4">Select Authorization Providers</h1>
141
149
  <p class="text-gray-600 mb-8">
@@ -167,23 +175,68 @@ function buildFederatedLoginPage(params) {
167
175
 
168
176
  <!-- Buttons -->
169
177
  <div class="flex gap-4">
170
- <button type="button"
171
- class="flex-1 px-6 py-3 text-gray-700 bg-gray-100 hover:bg-gray-200 font-medium rounded-lg transition-colors"
172
- onclick="document.querySelectorAll('input[name=providers]').forEach(cb => cb.checked = false); document.getElementById('federated-form').submit();">
173
- Skip All
174
- </button>
178
+ ${skipAllButton}
175
179
  <button type="submit"
176
180
  class="flex-1 px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors">
177
181
  Continue
178
182
  </button>
179
183
  </div>
180
184
  </form>
181
-
182
- <div class="mt-6 p-4 bg-blue-50 border border-blue-200 rounded-lg text-sm text-blue-800">
183
- Skipped providers can be authorized later when you try to use their tools (progressive authorization).
184
- </div>`;
185
+ ${skipNote}`;
185
186
  return wideLayout(content, { title: "Select Providers" });
186
187
  }
188
+ function buildFederatedStepPage(params) {
189
+ const { provider, step, clientName, pendingAuthId, callbackPath, csrfToken, error } = params;
190
+ const primaryBadge = provider.isPrimary ? `<span class="px-2 py-0.5 text-xs font-medium bg-blue-600 text-white rounded-full">Primary</span>` : "";
191
+ const providerUrl = provider.providerUrl ? `<p class="text-xs text-gray-400 font-mono truncate">${escapeHtml(provider.providerUrl)}</p>` : "";
192
+ const appIds = provider.appIds.length > 0 ? `<p class="text-xs text-gray-500 mt-1">Apps: ${provider.appIds.map((id) => escapeHtml(id)).join(", ")}</p>` : "";
193
+ const errorBanner = error ? `<div class="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg text-sm text-red-800">${escapeHtml(error)}</div>` : "";
194
+ const emailField = step.last ? `<div class="mb-6">
195
+ <label for="email" class="block text-sm font-medium text-gray-700 mb-2">Email</label>
196
+ <input type="email" id="email" name="email" required placeholder="you@example.com"
197
+ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
198
+ </div>` : "";
199
+ const secondaryButton = "flex-1 px-6 py-3 text-gray-700 bg-gray-100 hover:bg-gray-200 font-medium rounded-lg transition-colors";
200
+ const skipButton = step.canSkip ? `<button type="submit" name="action" value="skip" class="${secondaryButton}">Skip</button>` : "";
201
+ const backButton = step.canGoBack ? `<button type="submit" name="action" value="back" formnovalidate class="${secondaryButton}">Back</button>` : "";
202
+ const skipNote = step.canSkip ? `<div class="mt-6 p-4 bg-blue-50 border border-blue-200 rounded-lg text-sm text-blue-800">
203
+ A skipped provider can be authorized later when you try to use its tools (progressive authorization).
204
+ </div>` : "";
205
+ const content = `
206
+ <p class="text-sm font-medium text-blue-600 mb-2">Step ${step.index + 1} of ${step.count}</p>
207
+ <h1 class="text-3xl font-bold text-gray-900 mb-4">Authorize ${escapeHtml(provider.providerName)}</h1>
208
+ <p class="text-gray-600 mb-8">
209
+ ${escapeHtml(clientName)} uses multiple authentication providers. Authorize them one at a time.
210
+ </p>
211
+ ${errorBanner}
212
+ <form method="POST" action="${escapeHtml(callbackPath)}" id="federated-step-form">
213
+ <input type="hidden" name="pending_auth_id" value="${escapeHtml(pendingAuthId)}">
214
+ <input type="hidden" name="federated" value="true">
215
+ <input type="hidden" name="federated_step" value="${step.index}">
216
+ <input type="hidden" name="csrf" value="${escapeHtml(csrfToken)}">
217
+
218
+ <div class="p-4 bg-white border-2 border-blue-500 rounded-xl mb-8">
219
+ <div class="flex items-center gap-2 mb-1">
220
+ <span class="font-semibold text-gray-900">${escapeHtml(provider.providerName)}</span>
221
+ ${primaryBadge}
222
+ </div>
223
+ <p class="text-sm text-gray-500">Mode: ${escapeHtml(provider.mode)}</p>
224
+ ${providerUrl}
225
+ ${appIds}
226
+ </div>
227
+ ${emailField}
228
+ <div class="flex flex-row-reverse gap-4">
229
+ <button type="submit" name="action" value="continue"
230
+ class="flex-1 px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors">
231
+ ${step.last ? "Finish" : "Continue"}
232
+ </button>
233
+ ${skipButton}
234
+ ${backButton}
235
+ </div>
236
+ </form>
237
+ ${skipNote}`;
238
+ return wideLayout(content, { title: `Authorize ${provider.providerName}` });
239
+ }
187
240
  function buildToolConsentPage(params) {
188
241
  const {
189
242
  tools,
@@ -474,6 +527,7 @@ export {
474
527
  buildConsentPage,
475
528
  buildErrorPage,
476
529
  buildFederatedLoginPage,
530
+ buildFederatedStepPage,
477
531
  buildIncrementalAuthPage,
478
532
  buildLoginPage,
479
533
  buildToolConsentPage,
@@ -0,0 +1,6 @@
1
+ // libs/auth/src/upstream/index.ts
2
+ import { resolveUpstreamClient, UpstreamClientRegistrationError } from "./upstream-client-registration.mjs";
3
+ export {
4
+ UpstreamClientRegistrationError,
5
+ resolveUpstreamClient
6
+ };
@@ -0,0 +1,273 @@
1
+ // libs/auth/src/upstream/upstream-client-registration.ts
2
+ import { internalZ as z } from "@frontmcp/lazy-zod";
3
+ import {
4
+ base64urlDecode,
5
+ base64urlEncode,
6
+ decryptAesGcm,
7
+ encryptAesGcm,
8
+ hkdfSha256,
9
+ isProduction,
10
+ randomBytes,
11
+ sha256Hex,
12
+ StorageNotSupportedError
13
+ } from "@frontmcp/utils";
14
+ import { noopAuthLogger } from "../common/auth-logger.interface.mjs";
15
+ import { AuthInternalError } from "../errors/auth-internal.error.mjs";
16
+ import { normalizeIssuer, trimSlash } from "../jwks/jwks.utils.mjs";
17
+ var STORAGE_KEY_PREFIX = "upstream-client:";
18
+ var HKDF_SALT = new TextEncoder().encode("frontmcp-upstream-client");
19
+ var DEFAULT_TIMEOUT_MS = 1e4;
20
+ var SECRET_EXPIRY_MARGIN_SECONDS = 60;
21
+ var SUPPORTED_AUTH_METHODS = ["client_secret_post", "none"];
22
+ var UpstreamClientRegistrationError = class extends AuthInternalError {
23
+ constructor(provider, reason) {
24
+ super(
25
+ `Dynamic client registration with ${provider} failed: ${reason}. Register a client at the provider and set clientId (and clientSecret), or fix providerConfig.registrationEndpoint.`,
26
+ "UPSTREAM_CLIENT_REGISTRATION_FAILED"
27
+ );
28
+ }
29
+ };
30
+ var storedClientSchema = z.object({
31
+ clientId: z.string().min(1),
32
+ clientSecret: z.string().optional(),
33
+ clientSecretExpiresAt: z.number().optional(),
34
+ redirectUri: z.string()
35
+ });
36
+ var registrationResponseSchema = z.object({
37
+ client_id: z.string().min(1),
38
+ client_secret: z.string().optional(),
39
+ client_secret_expires_at: z.number().optional(),
40
+ redirect_uris: z.array(z.string()).optional(),
41
+ token_endpoint_auth_method: z.string().optional()
42
+ });
43
+ var metadataSchema = z.object({
44
+ issuer: z.string().optional(),
45
+ registration_endpoint: z.string().optional()
46
+ });
47
+ async function resolveUpstreamClient(options) {
48
+ const logger = options.logger ?? noopAuthLogger;
49
+ const { storage } = options;
50
+ const storageKey = upstreamClientStorageKey(options);
51
+ const cached = storage ? await readStoredClient(storage, options, storageKey) : { status: "missing" };
52
+ if (cached.status === "usable") {
53
+ logger.info(`Using client ${cached.client.clientId}, registered earlier with ${options.provider}`);
54
+ return cached.client;
55
+ }
56
+ const registered = await registerUpstreamClient(options);
57
+ logger.info(`Registered client ${registered.clientId} with ${options.provider}`);
58
+ if (!storage) {
59
+ logger.info("No persistent tokenStorage is configured, so the server registers again on its next start");
60
+ return registered;
61
+ }
62
+ return storeClient(storage, options, storageKey, registered, cached.status === "stale");
63
+ }
64
+ async function registerUpstreamClient(options) {
65
+ const endpoint = options.registrationEndpoint ?? await discoverRegistrationEndpoint(options);
66
+ assertFetchableUrl(options.provider, endpoint);
67
+ const requestBody = {
68
+ client_name: options.clientName,
69
+ redirect_uris: [options.redirectUri],
70
+ grant_types: ["authorization_code", "refresh_token"],
71
+ response_types: ["code"],
72
+ token_endpoint_auth_method: "client_secret_post",
73
+ ...options.scopes.length > 0 ? { scope: options.scopes.join(" ") } : {}
74
+ };
75
+ let response;
76
+ try {
77
+ response = await fetchWithTimeout(options, endpoint, {
78
+ method: "POST",
79
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
80
+ body: JSON.stringify(requestBody)
81
+ });
82
+ } catch (error) {
83
+ throw new UpstreamClientRegistrationError(
84
+ options.provider,
85
+ `${endpoint} could not be reached (${describe(error)})`
86
+ );
87
+ }
88
+ const responseBody = await response.json().catch(() => void 0);
89
+ if (!response.ok) {
90
+ throw new UpstreamClientRegistrationError(
91
+ options.provider,
92
+ `${endpoint} answered ${response.status}${describeOAuthError(responseBody)}`
93
+ );
94
+ }
95
+ const parsed = registrationResponseSchema.safeParse(responseBody);
96
+ if (!parsed.success) {
97
+ throw new UpstreamClientRegistrationError(options.provider, `${endpoint} answered without a client_id`);
98
+ }
99
+ const registration = parsed.data;
100
+ if (registration.redirect_uris && !registration.redirect_uris.includes(options.redirectUri)) {
101
+ throw new UpstreamClientRegistrationError(
102
+ options.provider,
103
+ `the provider registered other redirect URIs than ${options.redirectUri}`
104
+ );
105
+ }
106
+ const authMethod = registration.token_endpoint_auth_method;
107
+ if (authMethod && !SUPPORTED_AUTH_METHODS.includes(authMethod)) {
108
+ (options.logger ?? noopAuthLogger).warn(
109
+ `${options.provider} registered the client for token_endpoint_auth_method "${authMethod}"; FrontMCP sends the client secret in the token request body (client_secret_post)`
110
+ );
111
+ }
112
+ return {
113
+ clientId: registration.client_id,
114
+ clientSecret: registration.client_secret,
115
+ clientSecretExpiresAt: registration.client_secret_expires_at,
116
+ redirectUri: options.redirectUri
117
+ };
118
+ }
119
+ async function discoverRegistrationEndpoint(options) {
120
+ const logger = options.logger ?? noopAuthLogger;
121
+ const issuer = trimSlash(options.provider);
122
+ const issuerUrl = new URL(issuer);
123
+ const issuerPath = issuerUrl.pathname === "/" ? "" : issuerUrl.pathname;
124
+ const metadataUrls = [
125
+ `${issuerUrl.origin}/.well-known/oauth-authorization-server${issuerPath}`,
126
+ `${issuer}/.well-known/openid-configuration`
127
+ ];
128
+ for (const metadataUrl of metadataUrls) {
129
+ const metadata = await fetchMetadata(options, metadataUrl);
130
+ if (!metadata) continue;
131
+ if (normalizeIssuer(metadata.issuer) !== normalizeIssuer(issuer)) {
132
+ logger.warn(`Ignoring ${metadataUrl}: its issuer "${metadata.issuer ?? ""}" is not ${issuer}`);
133
+ continue;
134
+ }
135
+ if (metadata.registration_endpoint) return metadata.registration_endpoint;
136
+ }
137
+ throw new UpstreamClientRegistrationError(
138
+ options.provider,
139
+ `no registration_endpoint found in ${metadataUrls.join(" or ")}`
140
+ );
141
+ }
142
+ async function fetchMetadata(options, metadataUrl) {
143
+ assertFetchableUrl(options.provider, metadataUrl);
144
+ try {
145
+ const response = await fetchWithTimeout(options, metadataUrl, { headers: { Accept: "application/json" } });
146
+ if (!response.ok) return void 0;
147
+ const parsed = metadataSchema.safeParse(await response.json());
148
+ return parsed.success ? parsed.data : void 0;
149
+ } catch {
150
+ return void 0;
151
+ }
152
+ }
153
+ async function readStoredClient(storage, options, storageKey) {
154
+ const logger = options.logger ?? noopAuthLogger;
155
+ let sealed;
156
+ try {
157
+ sealed = await storage.get(storageKey);
158
+ } catch (error) {
159
+ logger.warn(`Could not read the stored upstream client (${describe(error)}); registering a new one`);
160
+ return { status: "missing" };
161
+ }
162
+ if (!sealed) return { status: "missing" };
163
+ const client = openClient(options.encryptionKey, storageKey, sealed);
164
+ if (!client) {
165
+ logger.warn("The stored upstream client could not be decrypted (did JWT_SECRET change?); registering a new one");
166
+ return { status: "stale" };
167
+ }
168
+ if (client.redirectUri !== options.redirectUri || isSecretExpired(client)) return { status: "stale" };
169
+ return { status: "usable", client };
170
+ }
171
+ async function storeClient(storage, options, storageKey, client, replace) {
172
+ const logger = options.logger ?? noopAuthLogger;
173
+ const sealed = sealClient(options.encryptionKey, storageKey, client);
174
+ const ttlSeconds = secretTtlSeconds(client);
175
+ try {
176
+ if (replace) {
177
+ await storage.set(storageKey, sealed, { ttlSeconds });
178
+ return client;
179
+ }
180
+ try {
181
+ await storage.set(storageKey, sealed, { ttlSeconds, ifNotExists: true });
182
+ } catch (error) {
183
+ if (!(error instanceof StorageNotSupportedError)) throw error;
184
+ await storage.set(storageKey, sealed, { ttlSeconds });
185
+ return client;
186
+ }
187
+ const stored = await storage.get(storageKey);
188
+ const winner = stored ? openClient(options.encryptionKey, storageKey, stored) : void 0;
189
+ if (winner && winner.clientId !== client.clientId) {
190
+ logger.info(`Another instance registered client ${winner.clientId} first; using it`);
191
+ return winner;
192
+ }
193
+ } catch (error) {
194
+ logger.warn(`Could not store the upstream client (${describe(error)}); it is used by this process only`);
195
+ }
196
+ return client;
197
+ }
198
+ function upstreamClientStorageKey(options) {
199
+ const scopes = [...options.scopes].sort().join(" ");
200
+ return `${STORAGE_KEY_PREFIX}${sha256Hex(`${normalizeIssuer(options.provider)}
201
+ ${options.redirectUri}
202
+ ${scopes}`)}`;
203
+ }
204
+ function recordKey(encryptionKey, storageKey) {
205
+ return hkdfSha256(encryptionKey, HKDF_SALT, new TextEncoder().encode(storageKey), 32);
206
+ }
207
+ function sealClient(encryptionKey, storageKey, client) {
208
+ const iv = randomBytes(12);
209
+ const plaintext = new TextEncoder().encode(JSON.stringify(client));
210
+ const { ciphertext, tag } = encryptAesGcm(recordKey(encryptionKey, storageKey), plaintext, iv);
211
+ return JSON.stringify({ iv: base64urlEncode(iv), tag: base64urlEncode(tag), data: base64urlEncode(ciphertext) });
212
+ }
213
+ function openClient(encryptionKey, storageKey, sealed) {
214
+ try {
215
+ const envelope = JSON.parse(sealed);
216
+ const plaintext = decryptAesGcm(
217
+ recordKey(encryptionKey, storageKey),
218
+ base64urlDecode(envelope.data),
219
+ base64urlDecode(envelope.iv),
220
+ base64urlDecode(envelope.tag)
221
+ );
222
+ const parsed = storedClientSchema.safeParse(JSON.parse(new TextDecoder().decode(plaintext)));
223
+ return parsed.success ? parsed.data : void 0;
224
+ } catch {
225
+ return void 0;
226
+ }
227
+ }
228
+ function isSecretExpired(client) {
229
+ const expiresAt = client.clientSecretExpiresAt;
230
+ if (!expiresAt) return false;
231
+ return expiresAt - SECRET_EXPIRY_MARGIN_SECONDS <= Math.floor(Date.now() / 1e3);
232
+ }
233
+ function secretTtlSeconds(client) {
234
+ const expiresAt = client.clientSecretExpiresAt;
235
+ if (!expiresAt) return void 0;
236
+ return Math.max(1, expiresAt - Math.floor(Date.now() / 1e3));
237
+ }
238
+ function assertFetchableUrl(provider, url) {
239
+ let parsed;
240
+ try {
241
+ parsed = new URL(url);
242
+ } catch {
243
+ throw new UpstreamClientRegistrationError(provider, `"${url}" is not a URL`);
244
+ }
245
+ const host = parsed.hostname.toLowerCase();
246
+ const isLoopback = host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host.endsWith(".localhost");
247
+ const allowed = parsed.protocol === "https:" || parsed.protocol === "http:" && (isLoopback || !isProduction());
248
+ if (!allowed) {
249
+ throw new UpstreamClientRegistrationError(provider, `${url} is not an https URL`);
250
+ }
251
+ }
252
+ async function fetchWithTimeout(options, url, init) {
253
+ const controller = new AbortController();
254
+ const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
255
+ try {
256
+ return await fetch(url, { ...init, redirect: "error", signal: controller.signal });
257
+ } finally {
258
+ clearTimeout(timer);
259
+ }
260
+ }
261
+ function describeOAuthError(body) {
262
+ if (!body || typeof body !== "object") return "";
263
+ const { error, error_description: description } = body;
264
+ if (typeof error !== "string") return "";
265
+ return typeof description === "string" ? `: ${error} (${description})` : `: ${error}`;
266
+ }
267
+ function describe(error) {
268
+ return error instanceof Error ? error.message : String(error);
269
+ }
270
+ export {
271
+ UpstreamClientRegistrationError,
272
+ resolveUpstreamClient
273
+ };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `private_key_jwt` client authentication (RFC 7523 §2.2 and §3, RFC 7521 §4.2) for clients whose client ID
3
+ * metadata document declares it: the client signs a short-lived JWT with a key from its `jwks` / `jwks_uri`.
4
+ */
5
+ import type { JSONWebKeySet } from 'jose';
6
+ /** The `client_assertion_type` of a JWT client assertion (RFC 7523 §2.2). */
7
+ export declare const CLIENT_ASSERTION_TYPE_JWT_BEARER = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
8
+ /** The signature algorithms a client assertion may use: asymmetric only, so never `none` or `HS*`. */
9
+ export declare const CLIENT_ASSERTION_SIGNING_ALGORITHMS: string[];
10
+ /** How far in the future an assertion's `exp` may be. */
11
+ export declare const MAX_CLIENT_ASSERTION_LIFETIME_SECONDS = 600;
12
+ /** What a token request carries to authenticate a `private_key_jwt` client. */
13
+ export interface ClientAssertionInput {
14
+ /** The request's `client_assertion_type`. */
15
+ type?: string;
16
+ /** The request's `client_assertion`. */
17
+ assertion?: string;
18
+ /** What the assertion's `aud` may name: this server's token endpoint and its issuer. */
19
+ audiences: readonly string[];
20
+ }
21
+ /** {@link ClientAssertionInput}, with the store that makes each assertion's `jti` single-use. */
22
+ export interface ClientAssertionCheck extends ClientAssertionInput {
23
+ /**
24
+ * Claim `key` for `ttlMs`, exactly once across the deployment.
25
+ * @returns `true` for the first claim, `false` when the key was already claimed or the store is unavailable
26
+ */
27
+ claimOnce(key: string, ttlMs: number): Promise<boolean>;
28
+ }
29
+ interface ClientAssertionContext extends ClientAssertionCheck {
30
+ clientId: string;
31
+ /** The client's keys; `refresh` asks for a fresh copy when the assertion names a key the cached one lacks. */
32
+ keys(refresh: boolean): Promise<JSONWebKeySet>;
33
+ }
34
+ /**
35
+ * Verify a client assertion: an asymmetric signature by one of the client's keys; `iss` and `sub` equal to the
36
+ * `client_id`; an `aud` naming this server; an `exp` at most {@link MAX_CLIENT_ASSERTION_LIFETIME_SECONDS} ahead;
37
+ * `nbf` and `iat` not in the future; and a `jti` never seen before within the assertion's lifetime.
38
+ *
39
+ * @throws CimdClientAssertionError naming the check that refused it
40
+ */
41
+ export declare function verifyClientAssertion(context: ClientAssertionContext): Promise<void>;
42
+ export {};
43
+ //# sourceMappingURL=cimd-client-assertion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cimd-client-assertion.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd-client-assertion.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,MAAM,CAAC;AAOtD,6EAA6E;AAC7E,eAAO,MAAM,gCAAgC,2DAA2D,CAAC;AAEzG,sGAAsG;AACtG,eAAO,MAAM,mCAAmC,UAY/C,CAAC;AAEF,yDAAyD;AACzD,eAAO,MAAM,qCAAqC,MAAM,CAAC;AAKzD,+EAA+E;AAC/E,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wFAAwF;IACxF,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B;AAED,iGAAiG;AACjG,MAAM,WAAW,oBAAqB,SAAQ,oBAAoB;IAChE;;;OAGG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACzD;AAED,UAAU,sBAAuB,SAAQ,oBAAoB;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,8GAA8G;IAC9G,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CAChD;AAID;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC1F"}
@@ -1,5 +1,5 @@
1
- import type { CimdCacheBackend, CimdCacheEntry, CimdCacheTtlConfig } from './cimd.cache';
2
- import type { ClientMetadataDocument, CimdCacheConfig } from './cimd.types';
1
+ import { type CimdCacheBackend, type CimdCacheEntry, type CimdCacheTtlConfig } from './cimd.cache';
2
+ import type { CimdCacheConfig, ClientMetadataDocument } from './cimd.types';
3
3
  /**
4
4
  * Redis-backed CIMD document cache.
5
5
  *
@@ -1 +1 @@
1
- {"version":3,"file":"cimd-redis.cache.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd-redis.cache.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEzF,OAAO,KAAK,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAa5E;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,cAAe,YAAW,gBAAgB;IACrD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;gBAElC,MAAM,EAAE,eAAe;IA+BnC;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAKhB;;;;;OAKG;IACG,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IA0BhE;;;;;OAKG;IACG,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAerE;;;;;;OAMG;IACG,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB9F;;;;;OAKG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IA4BtE;;;;;OAKG;IACG,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKhD;;;;;OAKG;IACG,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IA0B1F;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAK7B;;;;;;;;OAQG;IACG,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;IA2BhC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
1
+ {"version":3,"file":"cimd-redis.cache.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd-redis.cache.ts"],"names":[],"mappings":"AAQA,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAa5E;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,cAAe,YAAW,gBAAgB;IACrD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;gBAElC,MAAM,EAAE,eAAe;IA+BnC;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAKhB;;;;;OAKG;IACG,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IA0BhE;;;;;OAKG;IACG,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAerE;;;;;;OAMG;IACG,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB9F;;;;;OAKG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IA4BtE;;;;;OAKG;IACG,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKhD;;;;;OAKG;IACG,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IA0B1F;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAK7B;;;;;;;;OAQG;IACG,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;IA2BhC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Supports both in-memory and Redis backends.
8
8
  */
9
- import type { ClientMetadataDocument, CimdCacheConfig } from './cimd.types';
9
+ import type { CimdCacheConfig, ClientMetadataDocument } from './cimd.types';
10
10
  /**
11
11
  * Cache entry for a CIMD document.
12
12
  */
@@ -1 +1 @@
1
- {"version":3,"file":"cimd.cache.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd.cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cimd.cache.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd.cache.ts"],"names":[],"mappings":"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"}
@@ -115,6 +115,20 @@ export declare class CimdResponseTooLargeError extends CimdError {
115
115
  constructor(clientIdUrl: string, maxBytes: number, actualBytes?: number);
116
116
  getPublicMessage(): string;
117
117
  }
118
+ /**
119
+ * Which check refused a `private_key_jwt` client assertion. For the server's own logs: the client is told only
120
+ * that its authentication failed.
121
+ */
122
+ export type ClientAssertionRefusal = 'missing_assertion' | 'unsupported_assertion_type' | 'malformed' | 'unsupported_algorithm' | 'no_keys' | 'unknown_key' | 'bad_signature' | 'wrong_issuer' | 'wrong_subject' | 'wrong_audience' | 'missing_claim' | 'expired' | 'not_yet_valid' | 'lifetime_too_long' | 'replayed';
123
+ /**
124
+ * Thrown when a client that authenticates with `private_key_jwt` presents no valid client assertion
125
+ * (RFC 7523 §3). The token endpoint answers `invalid_client`.
126
+ */
127
+ export declare class CimdClientAssertionError extends CimdError {
128
+ readonly reason: ClientAssertionRefusal;
129
+ constructor(clientIdUrl: string, reason: ClientAssertionRefusal);
130
+ getPublicMessage(): string;
131
+ }
118
132
  /**
119
133
  * Thrown when CIMD is disabled but a CIMD client_id is used.
120
134
  */
@@ -1 +1 @@
1
- {"version":3,"file":"cimd.errors.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd.errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;GAKG;AACH,8BAAsB,SAAU,SAAQ,KAAK;IAC3C;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;gBAElB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;IAanF;;;OAGG;IACH,gBAAgB,IAAI,MAAM;CAG3B;AAED;;;;;;;GAOG;AACH,qBAAa,uBAAwB,SAAQ,SAAS;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAKtC,gBAAgB,IAAI,MAAM;CAGpC;AAED;;;;;;;GAOG;AACH,qBAAa,cAAe,SAAQ,SAAS;IAC3C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC;gBAEnB,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,KAAK,CAAA;KAAE;IAMjG,gBAAgB,IAAI,MAAM;CAMpC;AAED;;;;;;;GAOG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IAChD,QAAQ,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC;gBAExB,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE;IAKxC,gBAAgB,IAAI,MAAM;CAGpC;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACtD,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;gBAEtB,WAAW,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM;IAUhD,gBAAgB,IAAI,MAAM;CAGpC;AAED;;;;;;;GAOG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;gBAEpB,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAKtC,gBAAgB,IAAI,MAAM;CAGpC;AAED;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,SAAS;IACrD,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,EAAE,CAAC;gBAE3B,WAAW,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE;IAWnF,gBAAgB,IAAI,MAAM;CAGpC;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACtD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;gBAElB,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;IAa9D,gBAAgB,IAAI,MAAM;CAGpC;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;;CAI/C"}
1
+ {"version":3,"file":"cimd.errors.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd.errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;GAKG;AACH,8BAAsB,SAAU,SAAQ,KAAK;IAC3C;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;gBAElB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;IAanF;;;OAGG;IACH,gBAAgB,IAAI,MAAM;CAG3B;AAED;;;;;;;GAOG;AACH,qBAAa,uBAAwB,SAAQ,SAAS;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAKtC,gBAAgB,IAAI,MAAM;CAGpC;AAED;;;;;;;GAOG;AACH,qBAAa,cAAe,SAAQ,SAAS;IAC3C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC;gBAEnB,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,KAAK,CAAA;KAAE;IAMjG,gBAAgB,IAAI,MAAM;CAMpC;AAED;;;;;;;GAOG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IAChD,QAAQ,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC;gBAExB,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE;IAKxC,gBAAgB,IAAI,MAAM;CAGpC;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACtD,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;gBAEtB,WAAW,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM;IAUhD,gBAAgB,IAAI,MAAM;CAGpC;AAED;;;;;;;GAOG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;gBAEpB,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAKtC,gBAAgB,IAAI,MAAM;CAGpC;AAED;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,SAAS;IACrD,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,EAAE,CAAC;gBAE3B,WAAW,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE;IAWnF,gBAAgB,IAAI,MAAM;CAGpC;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACtD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;gBAElB,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;IAa9D,gBAAgB,IAAI,MAAM;CAGpC;AAED;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAC9B,mBAAmB,GACnB,4BAA4B,GAC5B,WAAW,GACX,uBAAuB,GACvB,SAAS,GACT,aAAa,GACb,eAAe,GACf,cAAc,GACd,eAAe,GACf,gBAAgB,GAChB,eAAe,GACf,SAAS,GACT,eAAe,GACf,mBAAmB,GACnB,UAAU,CAAC;AAEf;;;GAGG;AACH,qBAAa,wBAAyB,SAAQ,SAAS;IACrD,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC;gBAE5B,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB;IAKtD,gBAAgB,IAAI,MAAM;CAGpC;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;;CAI/C"}
@@ -1,3 +1,4 @@
1
+ import { type ClientAssertionCheck } from './cimd-client-assertion';
1
2
  import { type CimdLogger } from './cimd.logger';
2
3
  import { type CimdConfigInput, type CimdResolutionResult, type ClientMetadataDocument } from './cimd.types';
3
4
  /**
@@ -11,6 +12,7 @@ export declare class CimdService {
11
12
  private readonly securityConfig;
12
13
  private readonly networkConfig;
13
14
  private cacheBackend?;
15
+ private readonly keySets;
14
16
  private readonly logger;
15
17
  /**
16
18
  * Whether CIMD is enabled.
@@ -60,6 +62,34 @@ export declare class CimdService {
60
62
  * @throws RedirectUriMismatchError if the redirect_uri is not registered
61
63
  */
62
64
  validateRedirectUri(redirectUri: string, metadata: ClientMetadataDocument): void;
65
+ /**
66
+ * Check that this server can honor the client as its document declares it: it authenticates at the token
67
+ * endpoint with `none`, or with `private_key_jwt` and exactly one of `jwks` / `jwks_uri` (a public document holds
68
+ * no shared secret, so `client_secret_*` is refused), and it uses the authorization code grant with
69
+ * `response_type=code`.
70
+ *
71
+ * @param metadata - The client's metadata document
72
+ * @throws CimdValidationError naming each declaration this server cannot honor
73
+ */
74
+ assertSupportedClient(metadata: ClientMetadataDocument): void;
75
+ /**
76
+ * Authenticate a `private_key_jwt` client by its assertion (RFC 7523): see {@link verifyClientAssertion}.
77
+ *
78
+ * @param metadata - The client's metadata document, which declares `private_key_jwt`
79
+ * @param check - The token request's assertion, the audiences it may name, and the `jti` store
80
+ * @throws CimdClientAssertionError when the assertion is missing or refused
81
+ * @throws CimdFetchError, CimdResponseTooLargeError or CimdSecurityError when `jwks_uri` can't be fetched
82
+ */
83
+ verifyClientAssertion(metadata: ClientMetadataDocument, check: ClientAssertionCheck): Promise<void>;
84
+ /**
85
+ * The client's keys: its inline `jwks`, or the key set at its `jwks_uri`, fetched like the document itself
86
+ * (SSRF guard, redirect policy, timeout, size limit) and cached for the TTL its cache headers give, within
87
+ * `cache.minTtlMs` and `cache.maxTtlMs`. With `refresh`, a cached key set at least
88
+ * {@link KEY_SET_REFRESH_COOLDOWN_MS} old is fetched again.
89
+ */
90
+ private clientKeys;
91
+ /** Why a `private_key_jwt` document's keys can't be used: neither or both of `jwks` / `jwks_uri`, or a bad URI. */
92
+ private keySourceProblems;
63
93
  /**
64
94
  * Clear the cache for a specific client or all clients.
65
95
  *
@@ -73,11 +103,16 @@ export declare class CimdService {
73
103
  size: number;
74
104
  }>;
75
105
  /**
76
- * Fetch a metadata document from a CIMD URL.
106
+ * Fetch a metadata document from a CIMD URL, revalidating a cached one with a conditional request.
107
+ */
108
+ private fetchMetadataDocument;
109
+ /**
110
+ * Fetch a JSON document (a client metadata document or a client's `jwks_uri`) with the SSRF guard on every hop,
111
+ * the redirect policy, the timeout and the size limit. `url` names the document in every error.
77
112
  *
78
113
  * Following the JwksService.fetchJson() pattern.
79
114
  */
80
- private fetchMetadataDocument;
115
+ private fetchJson;
81
116
  /**
82
117
  * Read response body with size limit.
83
118
  */
@@ -1 +1 @@
1
- {"version":3,"file":"cimd.service.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd.service.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAQL,KAAK,eAAe,EAEpB,KAAK,oBAAoB,EAEzB,KAAK,sBAAsB,EAC5B,MAAM,cAAc,CAAC;AAGtB;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,YAAY,CAAC,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IAEpC;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;;;OAKG;gBACS,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC;IAyBlE;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC;;;OAGG;IACH,OAAO,CAAC,KAAK;IAUb,2FAA2F;IACrF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ9B;;;;;OAKG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIzC;;;;;;;;OAQG;IACG,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAqD5E;;;;;;OAMG;IACH,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,sBAAsB,GAAG,IAAI;IAUhF;;;;OAIG;IACG,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWlD;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAOhD;;;;OAIG;YACW,qBAAqB;IAqKnC;;OAEG;YACW,qBAAqB;IAyCnC;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;CAiBzB"}
1
+ {"version":3,"file":"cimd.service.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd.service.ts"],"names":[],"mappings":"AAWA,OAAO,EAAyB,KAAK,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAY3F,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EASL,KAAK,eAAe,EAEpB,KAAK,oBAAoB,EAEzB,KAAK,sBAAsB,EAC5B,MAAM,cAAc,CAAC;AAmBtB;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,YAAY,CAAC,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IAEpC;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;;;OAKG;gBACS,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC;IAyBlE;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC;;;OAGG;IACH,OAAO,CAAC,KAAK;IAUb,2FAA2F;IACrF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ9B;;;;;OAKG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIzC;;;;;;;;OAQG;IACG,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAqD5E;;;;;;OAMG;IACH,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,sBAAsB,GAAG,IAAI;IAUhF;;;;;;;;OAQG;IACH,qBAAqB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI;IAmB7D;;;;;;;OAOG;IACG,qBAAqB,CAAC,QAAQ,EAAE,sBAAsB,EAAE,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQzG;;;;;OAKG;YACW,UAAU;IA2BxB,mHAAmH;IACnH,OAAO,CAAC,iBAAiB;IAezB;;;;OAIG;IACG,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWlD;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAOhD;;OAEG;YACW,qBAAqB;IAenC;;;;;OAKG;YACW,SAAS;IAqJvB;;OAEG;YACW,qBAAqB;IAyCnC;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;CAiBzB"}
@@ -7,6 +7,12 @@
7
7
  * @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00
8
8
  */
9
9
  import { internalZ as z } from '@frontmcp/lazy-zod';
10
+ /**
11
+ * A client's JSON Web Key Set: its inline `jwks`, or the document at its `jwks_uri`.
12
+ */
13
+ export declare const clientJwksSchema: import("@frontmcp/lazy-zod").ZodObject<{
14
+ keys: import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodRecord<import("@frontmcp/lazy-zod").ZodString, import("@frontmcp/lazy-zod").ZodUnknown>>;
15
+ }, import("zod/v4/core").$strip>;
10
16
  /**
11
17
  * OAuth Client Metadata Document schema.
12
18
  *
@@ -1 +1 @@
1
- {"version":3,"file":"cimd.types.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd.types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,SAAS,IAAI,CAAC,EAAE,MAAM,oBAAoB,CAAC;AAMpD;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;gCAiGvC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAClF,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAMvF;;GAEG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;gCAwCrC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;gCA+BhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;GAEG;AACH,eAAO,MAAM,wBAAwB;;;;;;gCAoCnC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;gCA2BlC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAqB3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAM/D;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,YAAY,EAAE,OAAO,CAAC;IAEtB;;OAEG;IACH,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAElC;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
1
+ {"version":3,"file":"cimd.types.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd.types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,SAAS,IAAI,CAAC,EAAE,MAAM,oBAAoB,CAAC;AAMpD;;GAEG;AACH,eAAO,MAAM,gBAAgB;;gCAE3B,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;gCAgGvC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAClF,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAMvF;;GAEG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;gCAwCrC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;gCA+BhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;GAEG;AACH,eAAO,MAAM,wBAAwB;;;;;;gCAoCnC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;gCA2BlC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAqB3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAM/D;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,YAAY,EAAE,OAAO,CAAC;IAEtB;;OAEG;IACH,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAElC;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
package/cimd/index.d.ts CHANGED
@@ -7,8 +7,9 @@
7
7
  * @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00
8
8
  */
9
9
  export { type CimdLogger, noopLogger } from './cimd.logger';
10
- export { clientMetadataDocumentSchema, cimdCacheConfigSchema, cimdRedisCacheConfigSchema, cimdSecurityConfigSchema, cimdNetworkConfigSchema, cimdConfigSchema, type ClientMetadataDocument, type ClientMetadataDocumentInput, type CimdCacheConfig, type CimdRedisCacheConfig, type CimdSecurityConfig, type CimdNetworkConfig, type CimdConfig, type CimdConfigInput, type CimdResolutionResult, } from './cimd.types';
11
- export { CimdError, InvalidClientIdUrlError, CimdFetchError, CimdValidationError, CimdClientIdMismatchError, CimdSecurityError, RedirectUriMismatchError, CimdResponseTooLargeError, CimdDisabledError, } from './cimd.errors';
10
+ export { clientJwksSchema, clientMetadataDocumentSchema, cimdCacheConfigSchema, cimdRedisCacheConfigSchema, cimdSecurityConfigSchema, cimdNetworkConfigSchema, cimdConfigSchema, type ClientMetadataDocument, type ClientMetadataDocumentInput, type CimdCacheConfig, type CimdRedisCacheConfig, type CimdSecurityConfig, type CimdNetworkConfig, type CimdConfig, type CimdConfigInput, type CimdResolutionResult, } from './cimd.types';
11
+ export { CimdError, InvalidClientIdUrlError, CimdFetchError, CimdValidationError, CimdClientIdMismatchError, CimdSecurityError, RedirectUriMismatchError, CimdResponseTooLargeError, CimdDisabledError, CimdClientAssertionError, type ClientAssertionRefusal, } from './cimd.errors';
12
+ export { CLIENT_ASSERTION_TYPE_JWT_BEARER, CLIENT_ASSERTION_SIGNING_ALGORITHMS, MAX_CLIENT_ASSERTION_LIFETIME_SECONDS, type ClientAssertionInput, type ClientAssertionCheck, } from './cimd-client-assertion';
12
13
  export { isCimdClientId, validateClientIdUrl, checkSsrfProtection, hasOnlyLocalhostRedirectUris, } from './cimd.validator';
13
14
  export { InMemoryCimdCache, createCimdCache, extractCacheHeaders, parseCacheHeaders, type CimdCacheBackend, type CimdCacheEntry, type CimdCacheTtlConfig, type CacheableHeaders, } from './cimd.cache';
14
15
  export { RedisCimdCache } from './cimd-redis.cache';