@backstage/plugin-auth-backend 0.29.2-next.1 → 0.29.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.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @backstage/plugin-auth-backend
|
|
2
2
|
|
|
3
|
+
## 0.29.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e2b3472: Promoted Client ID Metadata Documents (CIMD) to the stable `auth.clientIdMetadataDocuments` configuration. The previous `auth.experimentalClientIdMetadataDocuments` key remains supported as a deprecated alias. Dynamic Client Registration now logs a deprecation warning when enabled and users should migrate to CIMD.
|
|
8
|
+
- 2aeb246: Added token revocation support for clients using client ID metadata documents (CIMD). The `/v1/revoke` endpoint is now available whenever dynamic client registration or client ID metadata documents are enabled, and is advertised through `revocation_endpoint` in the OpenID provider configuration.
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
- @backstage/plugin-auth-node@0.7.3
|
|
11
|
+
- @backstage/backend-plugin-api@1.9.3
|
|
12
|
+
- @backstage/plugin-catalog-node@2.2.3
|
|
13
|
+
|
|
3
14
|
## 0.29.2-next.1
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
package/config.schema.json
CHANGED
|
@@ -276,12 +276,19 @@
|
|
|
276
276
|
"items": {
|
|
277
277
|
"type": "string"
|
|
278
278
|
},
|
|
279
|
-
"description": "A list of allowed URI patterns to use for redirect URIs during dynamic client registration.
|
|
279
|
+
"description": "A list of allowed URI patterns to use for redirect URIs during dynamic client registration.\n\nPatterns are matched per URL component: a `*` in the hostname or path only matches within that component, and a `:*` port matches any port. Patterns must include an explicit protocol and only match the path they list, e.g. `http://localhost:*\\/*` allows any port and any path on localhost.\n\nDefaults to Cursor and loopback addresses (localhost, 127.0.0.1, [::1]).",
|
|
280
|
+
"examples": [
|
|
281
|
+
[
|
|
282
|
+
"http://localhost:*/*",
|
|
283
|
+
"https://*.example.com/callback"
|
|
284
|
+
]
|
|
285
|
+
]
|
|
280
286
|
}
|
|
281
287
|
},
|
|
282
|
-
"description": "Configuration for dynamic client registration"
|
|
288
|
+
"description": "Configuration for dynamic client registration",
|
|
289
|
+
"deprecated": "Use `auth.clientIdMetadataDocuments` instead."
|
|
283
290
|
},
|
|
284
|
-
"
|
|
291
|
+
"clientIdMetadataDocuments": {
|
|
285
292
|
"type": "object",
|
|
286
293
|
"properties": {
|
|
287
294
|
"enabled": {
|
|
@@ -316,6 +323,43 @@
|
|
|
316
323
|
}
|
|
317
324
|
},
|
|
318
325
|
"description": "Configuration for Client ID Metadata Documents (CIMD)"
|
|
326
|
+
},
|
|
327
|
+
"experimentalClientIdMetadataDocuments": {
|
|
328
|
+
"type": "object",
|
|
329
|
+
"properties": {
|
|
330
|
+
"enabled": {
|
|
331
|
+
"type": "boolean",
|
|
332
|
+
"description": "Whether to enable Client ID Metadata Documents support Defaults to false"
|
|
333
|
+
},
|
|
334
|
+
"allowedClientIdPatterns": {
|
|
335
|
+
"type": "array",
|
|
336
|
+
"items": {
|
|
337
|
+
"type": "string"
|
|
338
|
+
},
|
|
339
|
+
"description": "A list of allowed URI patterns for client_id URLs.\n\nPatterns are matched per URL component: a `*` in the hostname or path only matches within that component, and a `:*` port matches any port. Patterns must include an explicit protocol.\n\nDefaults to `['https://claude.ai/*', 'https://vscode.dev/*', '{baseUrl}/.well-known/oauth-client/cli.json']` where `{baseUrl}` is the auth backend's base URL.",
|
|
340
|
+
"examples": [
|
|
341
|
+
[
|
|
342
|
+
"https://example.com/*",
|
|
343
|
+
"https://*.trusted-domain.com/*"
|
|
344
|
+
]
|
|
345
|
+
]
|
|
346
|
+
},
|
|
347
|
+
"allowedRedirectUriPatterns": {
|
|
348
|
+
"type": "array",
|
|
349
|
+
"items": {
|
|
350
|
+
"type": "string"
|
|
351
|
+
},
|
|
352
|
+
"description": "A list of allowed URI patterns for redirect URIs.\n\nPatterns are matched per URL component: a `*` in the hostname or path only matches within that component, and a `:*` port matches any port. Patterns must include an explicit protocol and only match the path they list, e.g. `http://localhost:*\\/*` allows any port and any path on localhost.\n\nDefaults to loopback addresses (localhost, 127.0.0.1, [::1]).",
|
|
353
|
+
"examples": [
|
|
354
|
+
[
|
|
355
|
+
"http://localhost:*/*",
|
|
356
|
+
"http://127.0.0.1:*/callback"
|
|
357
|
+
]
|
|
358
|
+
]
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
"description": "Configuration for Client ID Metadata Documents (CIMD)",
|
|
362
|
+
"deprecated": "This is no longer experimental; use `auth.clientIdMetadataDocuments` instead."
|
|
319
363
|
}
|
|
320
364
|
},
|
|
321
365
|
"description": "Configuration options for the auth plugin"
|
|
@@ -135,6 +135,7 @@ class OidcRouter {
|
|
|
135
135
|
getRouter() {
|
|
136
136
|
const router = Router__default.default();
|
|
137
137
|
router.use(express.json());
|
|
138
|
+
const cimdEnabled = this.oidc.isCimdEnabled();
|
|
138
139
|
router.get("/.well-known/openid-configuration", (_req, res) => {
|
|
139
140
|
res.json(this.oidc.getConfiguration());
|
|
140
141
|
});
|
|
@@ -143,10 +144,7 @@ class OidcRouter {
|
|
|
143
144
|
res.json({ keys });
|
|
144
145
|
});
|
|
145
146
|
router.get("/.well-known/oauth-client/cli.json", (_req, res) => {
|
|
146
|
-
|
|
147
|
-
"auth.experimentalClientIdMetadataDocuments.enabled"
|
|
148
|
-
);
|
|
149
|
-
if (!cimdEnabled2) {
|
|
147
|
+
if (!cimdEnabled) {
|
|
150
148
|
res.status(404).json({
|
|
151
149
|
error: "not_found",
|
|
152
150
|
error_description: "Client ID metadata documents not enabled"
|
|
@@ -178,10 +176,12 @@ class OidcRouter {
|
|
|
178
176
|
});
|
|
179
177
|
const dcrEnabled = this.config.getOptionalBoolean(
|
|
180
178
|
"auth.experimentalDynamicClientRegistration.enabled"
|
|
181
|
-
);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
179
|
+
) ?? false;
|
|
180
|
+
if (dcrEnabled) {
|
|
181
|
+
this.logger.warn(
|
|
182
|
+
"DEPRECATION WARNING: The 'auth.experimentalDynamicClientRegistration' configuration is deprecated. Migrate to Client ID Metadata Documents (CIMD) using 'auth.clientIdMetadataDocuments'."
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
185
|
if (dcrEnabled || cimdEnabled) {
|
|
186
186
|
router.get("/v1/authorize", async (req, res) => {
|
|
187
187
|
const {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OidcRouter.cjs.js","sources":["../../src/service/OidcRouter.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport Router from 'express-promise-router';\nimport { OidcClientCredentials, OidcService } from './OidcService';\nimport { AuthenticationError, isError } from '@backstage/errors';\nimport {\n AuthService,\n HttpAuthService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { TokenIssuer } from '../identity/types';\nimport { UserInfoDatabase } from '../database/UserInfoDatabase';\nimport { OidcDatabase } from '../database/OidcDatabase';\nimport { OfflineAccessService } from './OfflineAccessService';\nimport { json } from 'express';\nimport { z } from 'zod/v4';\nimport { fromZodError } from 'zod-validation-error/v4';\nimport { OidcError } from './OidcError';\n\nfunction ensureTrailingSlash(url: string): string {\n return url.endsWith('/') ? url : `${url}/`;\n}\n\nconst authorizeQuerySchema = z.object({\n client_id: z.string().min(1),\n redirect_uri: z.string().url(),\n response_type: z.string().min(1),\n scope: z.string().optional(),\n state: z.string().optional(),\n nonce: z.string().optional(),\n code_challenge: z.string().optional(),\n code_challenge_method: z.string().optional(),\n});\n\nconst sessionIdParamSchema = z.object({\n sessionId: z.string().min(1),\n});\n\nconst tokenRequestBodySchema = z.object({\n grant_type: z.string().min(1),\n code: z.string().optional(),\n redirect_uri: z.string().url().optional(),\n code_verifier: z.string().optional(),\n refresh_token: z.string().optional(),\n client_id: z.string().optional(),\n client_secret: z.string().optional(),\n});\n\nconst registerRequestBodySchema = z.object({\n client_name: z.string().optional(),\n redirect_uris: z.array(z.string().url()).min(1),\n response_types: z.array(z.string()).optional(),\n grant_types: z.array(z.string()).optional(),\n scope: z.string().optional(),\n});\n\nconst revokeRequestBodySchema = z.object({\n token: z.string().min(1),\n token_type_hint: z.string().optional(),\n client_id: z.string().optional(),\n client_secret: z.string().optional(),\n});\n\nfunction validateRequest<T>(schema: z.ZodSchema<T>, data: unknown): T {\n const parseResult = schema.safeParse(data);\n if (!parseResult.success) {\n const errorMessage = fromZodError(parseResult.error).message;\n throw new OidcError('invalid_request', errorMessage, 400);\n }\n return parseResult.data;\n}\n\nfunction parseClientCredentials(opts: {\n req: { headers: { authorization?: string } };\n bodyClientId?: string;\n bodyClientSecret?: string;\n}): Partial<OidcClientCredentials> {\n const { req, bodyClientId, bodyClientSecret } = opts;\n\n const basicAuth = req.headers.authorization?.match(/^Basic[ ]+([^\\s]+)$/i);\n if (basicAuth) {\n try {\n const decoded = Buffer.from(basicAuth[1], 'base64').toString('utf8');\n const idx = decoded.indexOf(':');\n if (idx >= 0) {\n const clientId = decoded.slice(0, idx);\n const clientSecret = decoded.slice(idx + 1);\n if (clientId && clientSecret) {\n return { clientId, clientSecret };\n }\n }\n } catch {\n /* ignore */\n }\n }\n\n return { clientId: bodyClientId, clientSecret: bodyClientSecret };\n}\n\nasync function authenticateClient(opts: {\n req: { headers: { authorization?: string } };\n oidc: OidcService;\n bodyClientId?: string;\n bodyClientSecret?: string;\n}): Promise<Required<OidcClientCredentials>> {\n const { req, oidc, bodyClientId, bodyClientSecret } = opts;\n const { clientId, clientSecret } = parseClientCredentials({\n req,\n bodyClientId,\n bodyClientSecret,\n });\n\n if (!clientId || !clientSecret) {\n throw new OidcError(\n 'invalid_client',\n 'Client authentication required',\n 401,\n );\n }\n\n try {\n const isValidClient = await oidc.verifyClientCredentials({\n clientId,\n clientSecret,\n });\n if (!isValidClient) {\n throw new OidcError('invalid_client', 'Invalid client credentials', 401);\n }\n } catch (e) {\n throw OidcError.fromError(e);\n }\n\n return { clientId, clientSecret };\n}\n\nexport class OidcRouter {\n private readonly oidc: OidcService;\n private readonly logger: LoggerService;\n private readonly auth: AuthService;\n private readonly appUrl: string;\n private readonly httpAuth: HttpAuthService;\n private readonly config: RootConfigService;\n private readonly baseUrl: string;\n\n private constructor(\n oidc: OidcService,\n logger: LoggerService,\n auth: AuthService,\n appUrl: string,\n httpAuth: HttpAuthService,\n config: RootConfigService,\n baseUrl: string,\n ) {\n this.oidc = oidc;\n this.logger = logger;\n this.auth = auth;\n this.appUrl = appUrl;\n this.httpAuth = httpAuth;\n this.config = config;\n this.baseUrl = baseUrl;\n }\n\n static create(options: {\n auth: AuthService;\n tokenIssuer: TokenIssuer;\n baseUrl: string;\n appUrl: string;\n logger: LoggerService;\n userInfo: UserInfoDatabase;\n oidc: OidcDatabase;\n httpAuth: HttpAuthService;\n config: RootConfigService;\n offlineAccess?: OfflineAccessService;\n }) {\n return new OidcRouter(\n OidcService.create(options),\n options.logger,\n options.auth,\n options.appUrl,\n options.httpAuth,\n options.config,\n options.baseUrl,\n );\n }\n\n public getRouter() {\n const router = Router();\n\n router.use(json());\n\n // OpenID Provider Configuration endpoint\n // https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig\n // Returns the OpenID Provider Configuration document containing metadata about the provider\n router.get('/.well-known/openid-configuration', (_req, res) => {\n res.json(this.oidc.getConfiguration());\n });\n\n // JSON Web Key Set endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.10.1.1\n // Returns the public keys used to verify JWTs issued by this provider\n router.get('/.well-known/jwks.json', async (_req, res) => {\n const { keys } = await this.oidc.listPublicKeys();\n res.json({ keys });\n });\n\n // CIMD metadata endpoint for the Backstage CLI\n // Automatically available when CIMD is enabled\n router.get('/.well-known/oauth-client/cli.json', (_req, res) => {\n const cimdEnabled = this.config.getOptionalBoolean(\n 'auth.experimentalClientIdMetadataDocuments.enabled',\n );\n\n if (!cimdEnabled) {\n res.status(404).json({\n error: 'not_found',\n error_description: 'Client ID metadata documents not enabled',\n });\n return;\n }\n\n res.json({\n client_id: `${this.baseUrl}/.well-known/oauth-client/cli.json`,\n client_name: 'Backstage CLI',\n redirect_uris: ['http://127.0.0.1:8055/callback'],\n response_types: ['code'],\n grant_types: ['authorization_code'],\n token_endpoint_auth_method: 'none',\n scope: 'openid offline_access',\n });\n });\n\n // UserInfo endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#UserInfo\n // Returns claims about the authenticated user using an access token\n router.get('/v1/userinfo', async (req, res) => {\n const matches = req.headers.authorization?.match(/^Bearer[ ]+(\\S+)$/i);\n const token = matches?.[1];\n if (!token) {\n throw new AuthenticationError('No token provided');\n }\n\n const userInfo = await this.oidc.getUserInfo({ token });\n\n if (!userInfo) {\n res.status(404).send('User info not found');\n return;\n }\n\n res.json(userInfo);\n });\n\n const dcrEnabled = this.config.getOptionalBoolean(\n 'auth.experimentalDynamicClientRegistration.enabled',\n );\n const cimdEnabled = this.config.getOptionalBoolean(\n 'auth.experimentalClientIdMetadataDocuments.enabled',\n );\n\n if (dcrEnabled || cimdEnabled) {\n // Authorization endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest\n // Handles the initial authorization request from the client, validates parameters,\n // and redirects to the Authorization Session page for user approval\n router.get('/v1/authorize', async (req, res) => {\n const {\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: responseType,\n scope,\n state,\n nonce,\n code_challenge: codeChallenge,\n code_challenge_method: codeChallengeMethod,\n } = validateRequest(authorizeQuerySchema, req.query);\n\n try {\n const result = await this.oidc.createAuthorizationSession({\n clientId,\n redirectUri,\n responseType,\n scope,\n state,\n nonce,\n codeChallenge,\n codeChallengeMethod,\n });\n\n // todo(blam): maybe this URL could be overridable by config if\n // the plugin is mounted somewhere else?\n // support slashes in baseUrl?\n const authSessionRedirectUrl = new URL(\n `./oauth2/authorize/${result.id}`,\n ensureTrailingSlash(this.appUrl),\n );\n\n return res.redirect(authSessionRedirectUrl.toString());\n } catch (error) {\n if (OidcError.isOidcError(error)) {\n const errorParams = new URLSearchParams();\n errorParams.append('error', error.body.error);\n errorParams.append(\n 'error_description',\n error.body.error_description,\n );\n if (state) {\n errorParams.append('state', state);\n }\n\n const redirectUrl = new URL(redirectUri);\n redirectUrl.search = errorParams.toString();\n return res.redirect(redirectUrl.toString());\n }\n throw error;\n }\n });\n\n // Authorization Session request details endpoint\n // Returns Authorization Session request details for the frontend\n router.get('/v1/sessions/:sessionId', async (req, res) => {\n const { sessionId } = validateRequest(sessionIdParamSchema, req.params);\n\n try {\n const session = await this.oidc.getAuthorizationSession({\n sessionId,\n });\n\n return res.json({\n id: session.id,\n clientId: session.clientId,\n clientName: session.clientName,\n scope: session.scope,\n redirectUri: session.redirectUri,\n });\n } catch (error) {\n throw OidcError.fromError(error);\n }\n });\n\n // Authorization Session approval endpoint\n // Handles user approval of Authorization Session requests and generates authorization codes\n router.post('/v1/sessions/:sessionId/approve', async (req, res) => {\n const { sessionId } = validateRequest(sessionIdParamSchema, req.params);\n\n try {\n const httpCredentials = await this.httpAuth.credentials(req);\n\n if (!this.auth.isPrincipal(httpCredentials, 'user')) {\n throw new OidcError(\n 'access_denied',\n 'Authentication required',\n 403,\n );\n }\n\n const { userEntityRef } = httpCredentials.principal;\n\n const result = await this.oidc.approveAuthorizationSession({\n sessionId,\n userEntityRef,\n });\n\n return res.json({\n redirectUrl: result.redirectUrl,\n });\n } catch (error) {\n throw OidcError.fromError(error);\n }\n });\n\n // Authorization Session rejection endpoint\n // Handles user rejection of Authorization Session requests and redirects with error\n router.post('/v1/sessions/:sessionId/reject', async (req, res) => {\n const { sessionId } = validateRequest(sessionIdParamSchema, req.params);\n\n const httpCredentials = await this.httpAuth.credentials(req);\n\n if (!this.auth.isPrincipal(httpCredentials, 'user')) {\n throw new OidcError('access_denied', 'Authentication required', 403);\n }\n\n const { userEntityRef } = httpCredentials.principal;\n try {\n const session = await this.oidc.getAuthorizationSession({\n sessionId,\n });\n\n await this.oidc.rejectAuthorizationSession({\n sessionId,\n userEntityRef,\n });\n\n const errorParams = new URLSearchParams();\n errorParams.append('error', 'access_denied');\n errorParams.append('error_description', 'User denied the request');\n if (session.state) {\n errorParams.append('state', session.state);\n }\n\n const redirectUrl = new URL(session.redirectUri);\n redirectUrl.search = errorParams.toString();\n\n return res.json({\n redirectUrl: redirectUrl.toString(),\n });\n } catch (error) {\n throw OidcError.fromError(error);\n }\n });\n\n // Token endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest\n // Exchanges authorization codes for access tokens and ID tokens\n // Also handles refresh token grant type\n router.post('/v1/token', async (req, res) => {\n const {\n grant_type: grantType,\n code,\n redirect_uri: redirectUri,\n code_verifier: codeVerifier,\n refresh_token: refreshToken,\n client_id: bodyClientId,\n client_secret: bodyClientSecret,\n } = validateRequest(tokenRequestBodySchema, req.body);\n\n try {\n // Handle authorization_code grant type\n if (grantType === 'authorization_code') {\n if (!code || !redirectUri) {\n throw new OidcError(\n 'invalid_request',\n 'Missing code or redirect_uri parameters for authorization_code grant',\n 400,\n );\n }\n\n const result = await this.oidc.exchangeCodeForToken({\n code,\n redirectUri,\n codeVerifier,\n grantType,\n });\n\n return res.json({\n access_token: result.accessToken,\n token_type: result.tokenType,\n expires_in: result.expiresIn,\n id_token: result.idToken,\n scope: result.scope,\n ...(result.refreshToken && {\n refresh_token: result.refreshToken,\n }),\n });\n }\n\n // Handle refresh_token grant type\n if (grantType === 'refresh_token') {\n if (!refreshToken) {\n throw new OidcError(\n 'invalid_request',\n 'Missing refresh_token parameter for refresh_token grant',\n 400,\n );\n }\n\n // Authenticate if credentials are provided via Basic auth or body\n const hasCredentials =\n req.headers.authorization?.match(/^Basic[ ]+([^\\s]+)$/i) ||\n (bodyClientId && bodyClientSecret);\n\n let authenticatedClientId: string | undefined;\n if (hasCredentials) {\n const { clientId: authedId } = await authenticateClient({\n req,\n oidc: this.oidc,\n bodyClientId,\n bodyClientSecret,\n });\n authenticatedClientId = authedId;\n }\n\n const result = await this.oidc.refreshAccessToken({\n refreshToken,\n clientId: authenticatedClientId,\n });\n\n return res.json({\n access_token: result.accessToken,\n token_type: result.tokenType,\n expires_in: result.expiresIn,\n refresh_token: result.refreshToken,\n });\n }\n\n // Unsupported grant type\n throw new OidcError(\n 'unsupported_grant_type',\n `Grant type ${grantType} is not supported`,\n 400,\n );\n } catch (error) {\n // Invalid auth codes and refresh tokens should be invalid_grant, not invalid_client.\n // Client auth failures are already thrown as OidcError by authenticateClient.\n if (isError(error) && error.name === 'AuthenticationError') {\n throw new OidcError('invalid_grant', error.message, 400, error);\n }\n throw OidcError.fromError(error);\n }\n });\n\n // Token Revocation endpoint (RFC 7009-like)\n // Allows clients to revoke refresh tokens. DCR clients are confidential\n // and must authenticate with their client secret, while CIMD clients\n // are public clients that only identify themselves with their client ID\n router.post('/v1/revoke', async (req, res) => {\n try {\n const {\n token,\n client_id: bodyClientId,\n client_secret: bodyClientSecret,\n } = validateRequest(revokeRequestBodySchema, req.body ?? {});\n\n const { clientId, clientSecret } = parseClientCredentials({\n req,\n bodyClientId,\n bodyClientSecret,\n });\n\n if (!clientId) {\n throw new OidcError(\n 'invalid_client',\n 'Client authentication required',\n 401,\n );\n }\n\n const isValidClient = await this.oidc.verifyRevocationClient({\n clientId,\n clientSecret,\n });\n if (!isValidClient) {\n throw new OidcError(\n 'invalid_client',\n 'Invalid client credentials',\n 401,\n );\n }\n\n try {\n await this.oidc.revokeRefreshToken(token);\n } catch (e) {\n this.logger.error('Failed to revoke token', e);\n }\n\n return res.status(200).send('');\n } catch (e) {\n throw OidcError.fromError(e);\n }\n });\n }\n\n // Dynamic Client Registration endpoint - only available when DCR is enabled\n if (dcrEnabled) {\n // https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration\n // Allows clients to register themselves dynamically with the provider\n router.post('/v1/register', async (req, res) => {\n const {\n client_name: clientName,\n redirect_uris: redirectUris,\n response_types: responseTypes,\n grant_types: grantTypes,\n scope,\n } = validateRequest(registerRequestBodySchema, req.body);\n\n try {\n const client = await this.oidc.registerClient({\n clientName: clientName ?? 'Backstage CLI',\n redirectUris,\n responseTypes,\n grantTypes,\n scope,\n });\n\n return res.status(201).json({\n client_id: client.clientId,\n redirect_uris: client.redirectUris,\n client_secret: client.clientSecret,\n });\n } catch (e) {\n throw OidcError.fromError(e);\n }\n });\n }\n\n router.use(OidcError.middleware(this.logger));\n\n return router;\n }\n}\n"],"names":["z","fromZodError","OidcError","OidcService","Router","json","cimdEnabled","AuthenticationError","isError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OidcRouter.cjs.js","sources":["../../src/service/OidcRouter.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport Router from 'express-promise-router';\nimport { OidcClientCredentials, OidcService } from './OidcService';\nimport { AuthenticationError, isError } from '@backstage/errors';\nimport {\n AuthService,\n HttpAuthService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { TokenIssuer } from '../identity/types';\nimport { UserInfoDatabase } from '../database/UserInfoDatabase';\nimport { OidcDatabase } from '../database/OidcDatabase';\nimport { OfflineAccessService } from './OfflineAccessService';\nimport { json } from 'express';\nimport { z } from 'zod/v4';\nimport { fromZodError } from 'zod-validation-error/v4';\nimport { OidcError } from './OidcError';\n\nfunction ensureTrailingSlash(url: string): string {\n return url.endsWith('/') ? url : `${url}/`;\n}\n\nconst authorizeQuerySchema = z.object({\n client_id: z.string().min(1),\n redirect_uri: z.string().url(),\n response_type: z.string().min(1),\n scope: z.string().optional(),\n state: z.string().optional(),\n nonce: z.string().optional(),\n code_challenge: z.string().optional(),\n code_challenge_method: z.string().optional(),\n});\n\nconst sessionIdParamSchema = z.object({\n sessionId: z.string().min(1),\n});\n\nconst tokenRequestBodySchema = z.object({\n grant_type: z.string().min(1),\n code: z.string().optional(),\n redirect_uri: z.string().url().optional(),\n code_verifier: z.string().optional(),\n refresh_token: z.string().optional(),\n client_id: z.string().optional(),\n client_secret: z.string().optional(),\n});\n\nconst registerRequestBodySchema = z.object({\n client_name: z.string().optional(),\n redirect_uris: z.array(z.string().url()).min(1),\n response_types: z.array(z.string()).optional(),\n grant_types: z.array(z.string()).optional(),\n scope: z.string().optional(),\n});\n\nconst revokeRequestBodySchema = z.object({\n token: z.string().min(1),\n token_type_hint: z.string().optional(),\n client_id: z.string().optional(),\n client_secret: z.string().optional(),\n});\n\nfunction validateRequest<T>(schema: z.ZodSchema<T>, data: unknown): T {\n const parseResult = schema.safeParse(data);\n if (!parseResult.success) {\n const errorMessage = fromZodError(parseResult.error).message;\n throw new OidcError('invalid_request', errorMessage, 400);\n }\n return parseResult.data;\n}\n\nfunction parseClientCredentials(opts: {\n req: { headers: { authorization?: string } };\n bodyClientId?: string;\n bodyClientSecret?: string;\n}): Partial<OidcClientCredentials> {\n const { req, bodyClientId, bodyClientSecret } = opts;\n\n const basicAuth = req.headers.authorization?.match(/^Basic[ ]+([^\\s]+)$/i);\n if (basicAuth) {\n try {\n const decoded = Buffer.from(basicAuth[1], 'base64').toString('utf8');\n const idx = decoded.indexOf(':');\n if (idx >= 0) {\n const clientId = decoded.slice(0, idx);\n const clientSecret = decoded.slice(idx + 1);\n if (clientId && clientSecret) {\n return { clientId, clientSecret };\n }\n }\n } catch {\n /* ignore */\n }\n }\n\n return { clientId: bodyClientId, clientSecret: bodyClientSecret };\n}\n\nasync function authenticateClient(opts: {\n req: { headers: { authorization?: string } };\n oidc: OidcService;\n bodyClientId?: string;\n bodyClientSecret?: string;\n}): Promise<Required<OidcClientCredentials>> {\n const { req, oidc, bodyClientId, bodyClientSecret } = opts;\n const { clientId, clientSecret } = parseClientCredentials({\n req,\n bodyClientId,\n bodyClientSecret,\n });\n\n if (!clientId || !clientSecret) {\n throw new OidcError(\n 'invalid_client',\n 'Client authentication required',\n 401,\n );\n }\n\n try {\n const isValidClient = await oidc.verifyClientCredentials({\n clientId,\n clientSecret,\n });\n if (!isValidClient) {\n throw new OidcError('invalid_client', 'Invalid client credentials', 401);\n }\n } catch (e) {\n throw OidcError.fromError(e);\n }\n\n return { clientId, clientSecret };\n}\n\nexport class OidcRouter {\n private readonly oidc: OidcService;\n private readonly logger: LoggerService;\n private readonly auth: AuthService;\n private readonly appUrl: string;\n private readonly httpAuth: HttpAuthService;\n private readonly config: RootConfigService;\n private readonly baseUrl: string;\n\n private constructor(\n oidc: OidcService,\n logger: LoggerService,\n auth: AuthService,\n appUrl: string,\n httpAuth: HttpAuthService,\n config: RootConfigService,\n baseUrl: string,\n ) {\n this.oidc = oidc;\n this.logger = logger;\n this.auth = auth;\n this.appUrl = appUrl;\n this.httpAuth = httpAuth;\n this.config = config;\n this.baseUrl = baseUrl;\n }\n\n static create(options: {\n auth: AuthService;\n tokenIssuer: TokenIssuer;\n baseUrl: string;\n appUrl: string;\n logger: LoggerService;\n userInfo: UserInfoDatabase;\n oidc: OidcDatabase;\n httpAuth: HttpAuthService;\n config: RootConfigService;\n offlineAccess?: OfflineAccessService;\n }) {\n return new OidcRouter(\n OidcService.create(options),\n options.logger,\n options.auth,\n options.appUrl,\n options.httpAuth,\n options.config,\n options.baseUrl,\n );\n }\n\n public getRouter() {\n const router = Router();\n\n router.use(json());\n\n const cimdEnabled = this.oidc.isCimdEnabled();\n\n // OpenID Provider Configuration endpoint\n // https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig\n // Returns the OpenID Provider Configuration document containing metadata about the provider\n router.get('/.well-known/openid-configuration', (_req, res) => {\n res.json(this.oidc.getConfiguration());\n });\n\n // JSON Web Key Set endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.10.1.1\n // Returns the public keys used to verify JWTs issued by this provider\n router.get('/.well-known/jwks.json', async (_req, res) => {\n const { keys } = await this.oidc.listPublicKeys();\n res.json({ keys });\n });\n\n // CIMD metadata endpoint for the Backstage CLI\n // Automatically available when CIMD is enabled\n router.get('/.well-known/oauth-client/cli.json', (_req, res) => {\n if (!cimdEnabled) {\n res.status(404).json({\n error: 'not_found',\n error_description: 'Client ID metadata documents not enabled',\n });\n return;\n }\n\n res.json({\n client_id: `${this.baseUrl}/.well-known/oauth-client/cli.json`,\n client_name: 'Backstage CLI',\n redirect_uris: ['http://127.0.0.1:8055/callback'],\n response_types: ['code'],\n grant_types: ['authorization_code'],\n token_endpoint_auth_method: 'none',\n scope: 'openid offline_access',\n });\n });\n\n // UserInfo endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#UserInfo\n // Returns claims about the authenticated user using an access token\n router.get('/v1/userinfo', async (req, res) => {\n const matches = req.headers.authorization?.match(/^Bearer[ ]+(\\S+)$/i);\n const token = matches?.[1];\n if (!token) {\n throw new AuthenticationError('No token provided');\n }\n\n const userInfo = await this.oidc.getUserInfo({ token });\n\n if (!userInfo) {\n res.status(404).send('User info not found');\n return;\n }\n\n res.json(userInfo);\n });\n\n const dcrEnabled =\n this.config.getOptionalBoolean(\n 'auth.experimentalDynamicClientRegistration.enabled',\n ) ?? false;\n\n if (dcrEnabled) {\n this.logger.warn(\n \"DEPRECATION WARNING: The 'auth.experimentalDynamicClientRegistration' configuration is deprecated. Migrate to Client ID Metadata Documents (CIMD) using 'auth.clientIdMetadataDocuments'.\",\n );\n }\n\n if (dcrEnabled || cimdEnabled) {\n // Authorization endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest\n // Handles the initial authorization request from the client, validates parameters,\n // and redirects to the Authorization Session page for user approval\n router.get('/v1/authorize', async (req, res) => {\n const {\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: responseType,\n scope,\n state,\n nonce,\n code_challenge: codeChallenge,\n code_challenge_method: codeChallengeMethod,\n } = validateRequest(authorizeQuerySchema, req.query);\n\n try {\n const result = await this.oidc.createAuthorizationSession({\n clientId,\n redirectUri,\n responseType,\n scope,\n state,\n nonce,\n codeChallenge,\n codeChallengeMethod,\n });\n\n // todo(blam): maybe this URL could be overridable by config if\n // the plugin is mounted somewhere else?\n // support slashes in baseUrl?\n const authSessionRedirectUrl = new URL(\n `./oauth2/authorize/${result.id}`,\n ensureTrailingSlash(this.appUrl),\n );\n\n return res.redirect(authSessionRedirectUrl.toString());\n } catch (error) {\n if (OidcError.isOidcError(error)) {\n const errorParams = new URLSearchParams();\n errorParams.append('error', error.body.error);\n errorParams.append(\n 'error_description',\n error.body.error_description,\n );\n if (state) {\n errorParams.append('state', state);\n }\n\n const redirectUrl = new URL(redirectUri);\n redirectUrl.search = errorParams.toString();\n return res.redirect(redirectUrl.toString());\n }\n throw error;\n }\n });\n\n // Authorization Session request details endpoint\n // Returns Authorization Session request details for the frontend\n router.get('/v1/sessions/:sessionId', async (req, res) => {\n const { sessionId } = validateRequest(sessionIdParamSchema, req.params);\n\n try {\n const session = await this.oidc.getAuthorizationSession({\n sessionId,\n });\n\n return res.json({\n id: session.id,\n clientId: session.clientId,\n clientName: session.clientName,\n scope: session.scope,\n redirectUri: session.redirectUri,\n });\n } catch (error) {\n throw OidcError.fromError(error);\n }\n });\n\n // Authorization Session approval endpoint\n // Handles user approval of Authorization Session requests and generates authorization codes\n router.post('/v1/sessions/:sessionId/approve', async (req, res) => {\n const { sessionId } = validateRequest(sessionIdParamSchema, req.params);\n\n try {\n const httpCredentials = await this.httpAuth.credentials(req);\n\n if (!this.auth.isPrincipal(httpCredentials, 'user')) {\n throw new OidcError(\n 'access_denied',\n 'Authentication required',\n 403,\n );\n }\n\n const { userEntityRef } = httpCredentials.principal;\n\n const result = await this.oidc.approveAuthorizationSession({\n sessionId,\n userEntityRef,\n });\n\n return res.json({\n redirectUrl: result.redirectUrl,\n });\n } catch (error) {\n throw OidcError.fromError(error);\n }\n });\n\n // Authorization Session rejection endpoint\n // Handles user rejection of Authorization Session requests and redirects with error\n router.post('/v1/sessions/:sessionId/reject', async (req, res) => {\n const { sessionId } = validateRequest(sessionIdParamSchema, req.params);\n\n const httpCredentials = await this.httpAuth.credentials(req);\n\n if (!this.auth.isPrincipal(httpCredentials, 'user')) {\n throw new OidcError('access_denied', 'Authentication required', 403);\n }\n\n const { userEntityRef } = httpCredentials.principal;\n try {\n const session = await this.oidc.getAuthorizationSession({\n sessionId,\n });\n\n await this.oidc.rejectAuthorizationSession({\n sessionId,\n userEntityRef,\n });\n\n const errorParams = new URLSearchParams();\n errorParams.append('error', 'access_denied');\n errorParams.append('error_description', 'User denied the request');\n if (session.state) {\n errorParams.append('state', session.state);\n }\n\n const redirectUrl = new URL(session.redirectUri);\n redirectUrl.search = errorParams.toString();\n\n return res.json({\n redirectUrl: redirectUrl.toString(),\n });\n } catch (error) {\n throw OidcError.fromError(error);\n }\n });\n\n // Token endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest\n // Exchanges authorization codes for access tokens and ID tokens\n // Also handles refresh token grant type\n router.post('/v1/token', async (req, res) => {\n const {\n grant_type: grantType,\n code,\n redirect_uri: redirectUri,\n code_verifier: codeVerifier,\n refresh_token: refreshToken,\n client_id: bodyClientId,\n client_secret: bodyClientSecret,\n } = validateRequest(tokenRequestBodySchema, req.body);\n\n try {\n // Handle authorization_code grant type\n if (grantType === 'authorization_code') {\n if (!code || !redirectUri) {\n throw new OidcError(\n 'invalid_request',\n 'Missing code or redirect_uri parameters for authorization_code grant',\n 400,\n );\n }\n\n const result = await this.oidc.exchangeCodeForToken({\n code,\n redirectUri,\n codeVerifier,\n grantType,\n });\n\n return res.json({\n access_token: result.accessToken,\n token_type: result.tokenType,\n expires_in: result.expiresIn,\n id_token: result.idToken,\n scope: result.scope,\n ...(result.refreshToken && {\n refresh_token: result.refreshToken,\n }),\n });\n }\n\n // Handle refresh_token grant type\n if (grantType === 'refresh_token') {\n if (!refreshToken) {\n throw new OidcError(\n 'invalid_request',\n 'Missing refresh_token parameter for refresh_token grant',\n 400,\n );\n }\n\n // Authenticate if credentials are provided via Basic auth or body\n const hasCredentials =\n req.headers.authorization?.match(/^Basic[ ]+([^\\s]+)$/i) ||\n (bodyClientId && bodyClientSecret);\n\n let authenticatedClientId: string | undefined;\n if (hasCredentials) {\n const { clientId: authedId } = await authenticateClient({\n req,\n oidc: this.oidc,\n bodyClientId,\n bodyClientSecret,\n });\n authenticatedClientId = authedId;\n }\n\n const result = await this.oidc.refreshAccessToken({\n refreshToken,\n clientId: authenticatedClientId,\n });\n\n return res.json({\n access_token: result.accessToken,\n token_type: result.tokenType,\n expires_in: result.expiresIn,\n refresh_token: result.refreshToken,\n });\n }\n\n // Unsupported grant type\n throw new OidcError(\n 'unsupported_grant_type',\n `Grant type ${grantType} is not supported`,\n 400,\n );\n } catch (error) {\n // Invalid auth codes and refresh tokens should be invalid_grant, not invalid_client.\n // Client auth failures are already thrown as OidcError by authenticateClient.\n if (isError(error) && error.name === 'AuthenticationError') {\n throw new OidcError('invalid_grant', error.message, 400, error);\n }\n throw OidcError.fromError(error);\n }\n });\n\n // Token Revocation endpoint (RFC 7009-like)\n // Allows clients to revoke refresh tokens. DCR clients are confidential\n // and must authenticate with their client secret, while CIMD clients\n // are public clients that only identify themselves with their client ID\n router.post('/v1/revoke', async (req, res) => {\n try {\n const {\n token,\n client_id: bodyClientId,\n client_secret: bodyClientSecret,\n } = validateRequest(revokeRequestBodySchema, req.body ?? {});\n\n const { clientId, clientSecret } = parseClientCredentials({\n req,\n bodyClientId,\n bodyClientSecret,\n });\n\n if (!clientId) {\n throw new OidcError(\n 'invalid_client',\n 'Client authentication required',\n 401,\n );\n }\n\n const isValidClient = await this.oidc.verifyRevocationClient({\n clientId,\n clientSecret,\n });\n if (!isValidClient) {\n throw new OidcError(\n 'invalid_client',\n 'Invalid client credentials',\n 401,\n );\n }\n\n try {\n await this.oidc.revokeRefreshToken(token);\n } catch (e) {\n this.logger.error('Failed to revoke token', e);\n }\n\n return res.status(200).send('');\n } catch (e) {\n throw OidcError.fromError(e);\n }\n });\n }\n\n // Dynamic Client Registration endpoint - only available when DCR is enabled\n if (dcrEnabled) {\n // https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration\n // Allows clients to register themselves dynamically with the provider\n router.post('/v1/register', async (req, res) => {\n const {\n client_name: clientName,\n redirect_uris: redirectUris,\n response_types: responseTypes,\n grant_types: grantTypes,\n scope,\n } = validateRequest(registerRequestBodySchema, req.body);\n\n try {\n const client = await this.oidc.registerClient({\n clientName: clientName ?? 'Backstage CLI',\n redirectUris,\n responseTypes,\n grantTypes,\n scope,\n });\n\n return res.status(201).json({\n client_id: client.clientId,\n redirect_uris: client.redirectUris,\n client_secret: client.clientSecret,\n });\n } catch (e) {\n throw OidcError.fromError(e);\n }\n });\n }\n\n router.use(OidcError.middleware(this.logger));\n\n return router;\n }\n}\n"],"names":["z","fromZodError","OidcError","OidcService","Router","json","AuthenticationError","isError"],"mappings":"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"}
|
|
@@ -12,20 +12,48 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
|
|
|
12
12
|
var crypto__default = /*#__PURE__*/_interopDefaultCompat(crypto);
|
|
13
13
|
var matcher__default = /*#__PURE__*/_interopDefaultCompat(matcher);
|
|
14
14
|
|
|
15
|
+
const WILDCARD_PORT = /:\*(?=\/|$)/;
|
|
16
|
+
const EXPLICIT_PROTOCOL = /^[a-z][a-z0-9+.-]*:/i;
|
|
17
|
+
function createUrlPatternMatcher(pattern) {
|
|
18
|
+
if (pattern === "*") {
|
|
19
|
+
return () => true;
|
|
20
|
+
}
|
|
21
|
+
if (!EXPLICIT_PROTOCOL.test(pattern)) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`Invalid URL pattern '${pattern}', an explicit protocol is required`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
const hasWildcardPort = pattern.includes("://") && WILDCARD_PORT.test(pattern);
|
|
27
|
+
let patternUrl;
|
|
28
|
+
try {
|
|
29
|
+
patternUrl = new URL(
|
|
30
|
+
hasWildcardPort ? pattern.replace(WILDCARD_PORT, ":1") : pattern
|
|
31
|
+
);
|
|
32
|
+
} catch {
|
|
33
|
+
throw new Error(`Invalid URL pattern '${pattern}'`);
|
|
34
|
+
}
|
|
35
|
+
const hostnamePattern = patternUrl.hostname || "*";
|
|
36
|
+
const pathPattern = patternUrl.pathname || "*";
|
|
37
|
+
return (url) => url.protocol === patternUrl.protocol && matcher__default.default.isMatch(url.hostname, hostnamePattern) && (hasWildcardPort || url.port === patternUrl.port) && matcher__default.default.isMatch(url.pathname, pathPattern);
|
|
38
|
+
}
|
|
15
39
|
function validateRedirectUri(redirectUri, allowedPatterns) {
|
|
16
40
|
const parsed = new URL(redirectUri);
|
|
17
41
|
const normalized = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
|
|
18
|
-
if (
|
|
42
|
+
if (parsed.username || parsed.password) {
|
|
43
|
+
throw new errors.InputError(`Invalid redirect_uri '${normalized}'`);
|
|
44
|
+
}
|
|
45
|
+
const allowedMatchers = allowedPatterns.map(createUrlPatternMatcher);
|
|
46
|
+
if (!allowedMatchers.some((matches) => matches(parsed))) {
|
|
19
47
|
throw new errors.InputError(`Invalid redirect_uri '${normalized}'`);
|
|
20
48
|
}
|
|
21
49
|
}
|
|
22
50
|
const LOOPBACK_HOSTS = ["localhost", "127.0.0.1", "[::1]"];
|
|
23
51
|
const LOOPBACK_REDIRECT_PATTERNS = [
|
|
24
|
-
"http://localhost
|
|
52
|
+
"http://localhost:*/*",
|
|
25
53
|
"http://localhost/*",
|
|
26
|
-
"http://127.0.0.1
|
|
54
|
+
"http://127.0.0.1:*/*",
|
|
27
55
|
"http://127.0.0.1/*",
|
|
28
|
-
"http://[::1]
|
|
56
|
+
"http://[::1]:*/*",
|
|
29
57
|
"http://[::1]/*"
|
|
30
58
|
];
|
|
31
59
|
function matchesRedirectUri(requestUri, registeredUris) {
|
|
@@ -78,7 +106,7 @@ class OidcService {
|
|
|
78
106
|
const dcrEnabled = this.config.getOptionalBoolean(
|
|
79
107
|
"auth.experimentalDynamicClientRegistration.enabled"
|
|
80
108
|
);
|
|
81
|
-
const
|
|
109
|
+
const cimdEnabled = this.isCimdEnabled();
|
|
82
110
|
return {
|
|
83
111
|
issuer: this.baseUrl,
|
|
84
112
|
token_endpoint: `${this.baseUrl}/v1/token`,
|
|
@@ -123,6 +151,9 @@ class OidcService {
|
|
|
123
151
|
...cimdEnabled && { client_id_metadata_document_supported: true }
|
|
124
152
|
};
|
|
125
153
|
}
|
|
154
|
+
isCimdEnabled() {
|
|
155
|
+
return this.getCimdConfig().enabled;
|
|
156
|
+
}
|
|
126
157
|
async listPublicKeys() {
|
|
127
158
|
return await this.tokenIssuer.listPublicKeys();
|
|
128
159
|
}
|
|
@@ -211,17 +242,16 @@ class OidcService {
|
|
|
211
242
|
};
|
|
212
243
|
}
|
|
213
244
|
getCimdConfig() {
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
) ?? false;
|
|
245
|
+
const configPath = this.config.has("auth.clientIdMetadataDocuments") ? "auth.clientIdMetadataDocuments" : "auth.experimentalClientIdMetadataDocuments";
|
|
246
|
+
const enabled = this.config.getOptionalBoolean(`${configPath}.enabled`) ?? false;
|
|
217
247
|
const cliClientId = `${this.baseUrl}/.well-known/oauth-client/cli.json`;
|
|
218
248
|
return {
|
|
219
249
|
enabled,
|
|
220
250
|
allowedClientIdPatterns: this.config.getOptionalStringArray(
|
|
221
|
-
|
|
251
|
+
`${configPath}.allowedClientIdPatterns`
|
|
222
252
|
) ?? ["https://claude.ai/*", "https://vscode.dev/*", cliClientId],
|
|
223
253
|
allowedRedirectUriPatterns: this.config.getOptionalStringArray(
|
|
224
|
-
|
|
254
|
+
`${configPath}.allowedRedirectUriPatterns`
|
|
225
255
|
) ?? LOOPBACK_REDIRECT_PATTERNS
|
|
226
256
|
};
|
|
227
257
|
}
|
|
@@ -241,9 +271,10 @@ class OidcService {
|
|
|
241
271
|
if (!cimd.enabled) {
|
|
242
272
|
throw new errors.InputError("Client ID metadata documents not enabled");
|
|
243
273
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
)
|
|
274
|
+
const clientIdMatchers = cimd.allowedClientIdPatterns.map(
|
|
275
|
+
createUrlPatternMatcher
|
|
276
|
+
);
|
|
277
|
+
if (!clientIdMatchers.some((matches) => matches(opts.cimdUrl))) {
|
|
247
278
|
throw new errors.InputError(`Invalid client_id '${opts.clientId}'`);
|
|
248
279
|
}
|
|
249
280
|
const cimdClient = await CimdClient.fetchCimdMetadata({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OidcService.cjs.js","sources":["../../src/service/OidcService.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n AuthService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { TokenIssuer } from '../identity/types';\nimport { UserInfoDatabase } from '../database/UserInfoDatabase';\nimport {\n AuthenticationError,\n InputError,\n NotFoundError,\n} from '@backstage/errors';\nimport { decodeJwt } from 'jose';\nimport crypto from 'node:crypto';\nimport { OidcDatabase } from '../database/OidcDatabase';\nimport { DateTime } from 'luxon';\nimport matcher from 'matcher';\nimport { OfflineAccessService } from './OfflineAccessService';\nimport { validateCimdUrl, fetchCimdMetadata } from './CimdClient';\n\nfunction validateRedirectUri(\n redirectUri: string,\n allowedPatterns: string[],\n): void {\n const parsed = new URL(redirectUri);\n const normalized = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;\n\n if (!allowedPatterns.some(pattern => matcher.isMatch(normalized, pattern))) {\n throw new InputError(`Invalid redirect_uri '${normalized}'`);\n }\n}\n\nconst LOOPBACK_HOSTS = ['localhost', '127.0.0.1', '[::1]'];\nconst LOOPBACK_REDIRECT_PATTERNS = [\n 'http://localhost:*',\n 'http://localhost/*',\n 'http://127.0.0.1:*',\n 'http://127.0.0.1/*',\n 'http://[::1]:*',\n 'http://[::1]/*',\n];\n\n/**\n * RFC 8252 Section 7.3: For loopback redirect URIs, the authorization server\n * MUST allow any port to be specified at the time of the request. This matches\n * redirect URIs by scheme, hostname, and path only, ignoring the port.\n */\nfunction matchesRedirectUri(\n requestUri: string,\n registeredUris: string[],\n): boolean {\n const requested = new URL(requestUri);\n\n if (!LOOPBACK_HOSTS.includes(requested.hostname)) {\n return registeredUris.includes(requestUri);\n }\n\n return registeredUris.some(registered => {\n let reg: URL;\n try {\n reg = new URL(registered);\n } catch {\n return false;\n }\n return (\n LOOPBACK_HOSTS.includes(reg.hostname) &&\n reg.protocol === requested.protocol &&\n reg.hostname === requested.hostname &&\n reg.pathname === requested.pathname\n );\n });\n}\n\n/**\n * Credentials presented by an OAuth client. Confidential clients authenticate\n * with both a client ID and a client secret, while public clients identify\n * themselves with a client ID alone.\n */\nexport type OidcClientCredentials = {\n clientId: string;\n clientSecret?: string;\n};\n\nexport class OidcService {\n private readonly auth: AuthService;\n private readonly tokenIssuer: TokenIssuer;\n private readonly baseUrl: string;\n private readonly userInfo: UserInfoDatabase;\n private readonly oidc: OidcDatabase;\n private readonly config: RootConfigService;\n private readonly logger: LoggerService;\n private readonly offlineAccess?: OfflineAccessService;\n\n private constructor(\n auth: AuthService,\n tokenIssuer: TokenIssuer,\n baseUrl: string,\n userInfo: UserInfoDatabase,\n oidc: OidcDatabase,\n config: RootConfigService,\n logger: LoggerService,\n offlineAccess?: OfflineAccessService,\n ) {\n this.auth = auth;\n this.tokenIssuer = tokenIssuer;\n this.baseUrl = baseUrl;\n this.userInfo = userInfo;\n this.oidc = oidc;\n this.config = config;\n this.logger = logger;\n this.offlineAccess = offlineAccess;\n }\n\n static create(options: {\n auth: AuthService;\n tokenIssuer: TokenIssuer;\n baseUrl: string;\n userInfo: UserInfoDatabase;\n oidc: OidcDatabase;\n config: RootConfigService;\n logger: LoggerService;\n offlineAccess?: OfflineAccessService;\n }) {\n return new OidcService(\n options.auth,\n options.tokenIssuer,\n options.baseUrl,\n options.userInfo,\n options.oidc,\n options.config,\n options.logger,\n options.offlineAccess,\n );\n }\n\n public getConfiguration() {\n const dcrEnabled = this.config.getOptionalBoolean(\n 'auth.experimentalDynamicClientRegistration.enabled',\n );\n const { enabled: cimdEnabled } = this.getCimdConfig();\n\n return {\n issuer: this.baseUrl,\n token_endpoint: `${this.baseUrl}/v1/token`,\n userinfo_endpoint: `${this.baseUrl}/v1/userinfo`,\n jwks_uri: `${this.baseUrl}/.well-known/jwks.json`,\n response_types_supported: ['code', 'id_token'],\n subject_types_supported: ['public'],\n id_token_signing_alg_values_supported: [\n 'RS256',\n 'RS384',\n 'RS512',\n 'ES256',\n 'ES384',\n 'ES512',\n 'PS256',\n 'PS384',\n 'PS512',\n 'EdDSA',\n ],\n scopes_supported: [\n 'openid',\n ...(this.offlineAccess ? ['offline_access'] : []),\n ],\n token_endpoint_auth_methods_supported: [\n 'client_secret_basic',\n 'client_secret_post',\n ...(cimdEnabled ? ['none'] : []),\n ],\n claims_supported: ['sub', 'ent'],\n grant_types_supported: [\n 'authorization_code',\n ...(this.offlineAccess ? ['refresh_token'] : []),\n ],\n authorization_endpoint: `${this.baseUrl}/v1/authorize`,\n code_challenge_methods_supported: ['S256', 'plain'],\n ...(dcrEnabled && {\n registration_endpoint: `${this.baseUrl}/v1/register`,\n }),\n ...((dcrEnabled || cimdEnabled) && {\n revocation_endpoint: `${this.baseUrl}/v1/revoke`,\n }),\n ...(cimdEnabled && { client_id_metadata_document_supported: true }),\n };\n }\n\n public async listPublicKeys() {\n return await this.tokenIssuer.listPublicKeys();\n }\n\n public async getUserInfo({ token }: { token: string }) {\n const credentials = await this.auth.authenticate(token, {\n allowLimitedAccess: true,\n });\n if (!this.auth.isPrincipal(credentials, 'user')) {\n throw new InputError(\n 'Userinfo endpoint must be called with a token that represents a user principal',\n );\n }\n\n const { sub: userEntityRef } = decodeJwt(token);\n\n if (typeof userEntityRef !== 'string') {\n throw new Error('Invalid user token, user entity ref must be a string');\n }\n return await this.userInfo.getUserInfo(userEntityRef);\n }\n\n public async registerClient(opts: {\n responseTypes?: string[];\n grantTypes?: string[];\n clientName: string;\n redirectUris?: string[];\n scope?: string;\n }) {\n const generatedClientId = crypto.randomUUID();\n const generatedClientSecret = crypto.randomUUID();\n\n const allowedRedirectUriPatterns = this.config.getOptionalStringArray(\n 'auth.experimentalDynamicClientRegistration.allowedRedirectUriPatterns',\n ) ?? [\n 'cursor://*',\n 'https://www.cursor.com/*',\n ...LOOPBACK_REDIRECT_PATTERNS,\n ];\n\n for (const redirectUri of opts.redirectUris ?? []) {\n validateRedirectUri(redirectUri, allowedRedirectUriPatterns);\n }\n\n return await this.oidc.createClient({\n clientId: generatedClientId,\n clientName: opts.clientName,\n clientSecret: generatedClientSecret,\n redirectUris: opts.redirectUris ?? [],\n responseTypes: opts.responseTypes ?? ['code'],\n grantTypes: opts.grantTypes ?? ['authorization_code'],\n scope: opts.scope,\n });\n }\n\n public async createAuthorizationSession(opts: {\n clientId: string;\n redirectUri: string;\n responseType: string;\n scope?: string;\n state?: string;\n nonce?: string;\n codeChallenge?: string;\n codeChallengeMethod?: string;\n }) {\n const {\n clientId,\n redirectUri,\n responseType,\n scope,\n state,\n nonce,\n codeChallenge,\n codeChallengeMethod,\n } = opts;\n\n if (responseType !== 'code') {\n throw new InputError('Only authorization code flow is supported');\n }\n\n const client = await this.resolveClient({ clientId, redirectUri });\n\n if (client.requiresPkce && !codeChallenge) {\n throw new InputError(\n 'PKCE is required for public clients. Provide a code_challenge parameter.',\n );\n }\n\n if (codeChallenge) {\n if (\n !codeChallengeMethod ||\n !['S256', 'plain'].includes(codeChallengeMethod)\n ) {\n throw new InputError('Invalid code_challenge_method');\n }\n }\n\n const sessionId = crypto.randomUUID();\n const sessionExpiresAt = DateTime.now().plus({ hours: 1 }).toJSDate();\n\n await this.oidc.createAuthorizationSession({\n id: sessionId,\n clientId,\n redirectUri,\n responseType,\n scope,\n state,\n codeChallenge,\n codeChallengeMethod,\n nonce,\n expiresAt: sessionExpiresAt,\n });\n\n return {\n id: sessionId,\n clientName: client.clientName,\n scope,\n redirectUri,\n };\n }\n\n private getCimdConfig() {\n const enabled =\n this.config.getOptionalBoolean(\n 'auth.experimentalClientIdMetadataDocuments.enabled',\n ) ?? false;\n\n const cliClientId = `${this.baseUrl}/.well-known/oauth-client/cli.json`;\n\n return {\n enabled,\n allowedClientIdPatterns: this.config.getOptionalStringArray(\n 'auth.experimentalClientIdMetadataDocuments.allowedClientIdPatterns',\n ) ?? ['https://claude.ai/*', 'https://vscode.dev/*', cliClientId],\n allowedRedirectUriPatterns:\n this.config.getOptionalStringArray(\n 'auth.experimentalClientIdMetadataDocuments.allowedRedirectUriPatterns',\n ) ?? LOOPBACK_REDIRECT_PATTERNS,\n };\n }\n\n private async resolveClient(opts: {\n clientId: string;\n redirectUri?: string;\n }) {\n let cimdUrl: URL | undefined;\n try {\n cimdUrl = validateCimdUrl(opts.clientId);\n } catch {\n // Not a valid CIMD URL, fall through to DCR\n }\n\n if (cimdUrl) {\n return this.resolveCimdClient({ ...opts, cimdUrl });\n }\n return this.resolveDcrClient(opts);\n }\n\n private async resolveCimdClient(opts: {\n clientId: string;\n cimdUrl: URL;\n redirectUri?: string;\n }) {\n const cimd = this.getCimdConfig();\n\n if (!cimd.enabled) {\n throw new InputError('Client ID metadata documents not enabled');\n }\n\n if (\n !cimd.allowedClientIdPatterns.some(pattern =>\n matcher.isMatch(opts.clientId, pattern),\n )\n ) {\n throw new InputError(`Invalid client_id '${opts.clientId}'`);\n }\n\n const cimdClient = await fetchCimdMetadata({\n clientId: opts.clientId,\n validatedUrl: opts.cimdUrl,\n });\n\n if (opts.redirectUri) {\n validateRedirectUri(opts.redirectUri, cimd.allowedRedirectUriPatterns);\n\n if (!matchesRedirectUri(opts.redirectUri, cimdClient.redirectUris)) {\n throw new InputError(\n `Invalid redirect_uri '${opts.redirectUri}', not registered in client metadata`,\n );\n }\n }\n\n return {\n clientName: cimdClient.clientName,\n redirectUris: cimdClient.redirectUris,\n requiresPkce: true,\n };\n }\n\n private async resolveDcrClient(opts: {\n clientId: string;\n redirectUri?: string;\n }) {\n const client = await this.oidc.getClient({ clientId: opts.clientId });\n if (!client) {\n throw new InputError('Invalid client_id');\n }\n\n if (opts.redirectUri && !client.redirectUris.includes(opts.redirectUri)) {\n throw new InputError(`Invalid redirect_uri '${opts.redirectUri}'`);\n }\n\n return {\n clientName: client.clientName,\n redirectUris: client.redirectUris,\n requiresPkce: false,\n };\n }\n\n private async getValidPendingSession(sessionId: string) {\n const session = await this.oidc.getAuthorizationSession({ id: sessionId });\n\n if (!session) {\n throw new NotFoundError('Invalid authorization session');\n }\n\n if (DateTime.fromJSDate(session.expiresAt) < DateTime.now()) {\n throw new InputError('Authorization session expired');\n }\n\n if (session.status !== 'pending') {\n throw new NotFoundError('Authorization session not found or expired');\n }\n\n return session;\n }\n\n public async approveAuthorizationSession(opts: {\n sessionId: string;\n userEntityRef: string;\n }) {\n const { sessionId, userEntityRef } = opts;\n const session = await this.getValidPendingSession(sessionId);\n\n await this.oidc.updateAuthorizationSession({\n id: session.id,\n userEntityRef,\n status: 'approved',\n });\n\n const authorizationCode = crypto.randomBytes(32).toString('base64url');\n const codeExpiresAt = DateTime.now().plus({ minutes: 10 }).toJSDate();\n\n await this.oidc.createAuthorizationCode({\n code: authorizationCode,\n sessionId: session.id,\n expiresAt: codeExpiresAt,\n });\n\n const redirectUrl = new URL(session.redirectUri);\n\n redirectUrl.searchParams.append('code', authorizationCode);\n if (session.state) {\n redirectUrl.searchParams.append('state', session.state);\n }\n\n return {\n redirectUrl: redirectUrl.toString(),\n };\n }\n\n public async getAuthorizationSession(opts: { sessionId: string }) {\n const session = await this.getValidPendingSession(opts.sessionId);\n const { clientName } = await this.resolveClient({\n clientId: session.clientId,\n });\n\n return {\n id: session.id,\n clientId: session.clientId,\n clientName,\n redirectUri: session.redirectUri,\n scope: session.scope,\n state: session.state,\n responseType: session.responseType,\n codeChallenge: session.codeChallenge,\n codeChallengeMethod: session.codeChallengeMethod,\n nonce: session.nonce,\n expiresAt: session.expiresAt,\n status: session.status,\n };\n }\n\n public async rejectAuthorizationSession(opts: {\n sessionId: string;\n userEntityRef: string;\n }) {\n const { sessionId, userEntityRef } = opts;\n const session = await this.getValidPendingSession(sessionId);\n\n await this.oidc.updateAuthorizationSession({\n id: session.id,\n status: 'rejected',\n userEntityRef,\n });\n }\n\n public async exchangeCodeForToken(params: {\n code: string;\n redirectUri: string;\n codeVerifier?: string;\n grantType: string;\n }) {\n const { code, redirectUri, codeVerifier, grantType } = params;\n\n if (grantType !== 'authorization_code') {\n throw new InputError('Unsupported grant type');\n }\n\n const authCode = await this.oidc.getAuthorizationCode({ code });\n if (!authCode) {\n throw new AuthenticationError('Invalid authorization code');\n }\n\n if (DateTime.fromJSDate(authCode.expiresAt) < DateTime.now()) {\n throw new AuthenticationError('Authorization code expired');\n }\n\n if (authCode.used) {\n throw new AuthenticationError('Authorization code already used');\n }\n\n const session = await this.oidc.getAuthorizationSession({\n id: authCode.sessionId,\n });\n\n if (!session) {\n throw new NotFoundError('Invalid authorization session');\n }\n\n if (session.redirectUri !== redirectUri) {\n throw new AuthenticationError('Redirect URI mismatch');\n }\n\n if (session.status !== 'approved') {\n throw new AuthenticationError('Authorization not approved');\n }\n\n if (!session.userEntityRef) {\n throw new AuthenticationError('No user associated with authorization');\n }\n\n if (session.codeChallenge) {\n if (!codeVerifier) {\n throw new AuthenticationError('Code verifier required for PKCE');\n }\n\n if (\n !this.verifyPkce({\n codeChallenge: session.codeChallenge,\n codeVerifier,\n method: session.codeChallengeMethod,\n })\n ) {\n throw new AuthenticationError('Invalid code verifier');\n }\n }\n\n await this.oidc.updateAuthorizationCode({\n code,\n used: true,\n });\n\n const { token } = await this.tokenIssuer.issueToken({\n claims: {\n sub: session.userEntityRef,\n },\n });\n\n // Check if offline_access scope is requested\n let refreshToken: string | undefined;\n const scopes = session.scope?.split(' ') ?? [];\n if (scopes.includes('offline_access') && this.offlineAccess) {\n try {\n refreshToken = await this.offlineAccess.issueRefreshToken({\n userEntityRef: session.userEntityRef,\n oidcClientId: session.clientId,\n });\n } catch (err) {\n // Don't fail the entire token exchange if refresh token issuance fails.\n // The access token is still valid and should be returned.\n this.logger.warn(\n `Failed to issue refresh token for user ${session.userEntityRef}, offline_access will not be available: ${err}`,\n );\n }\n }\n\n return {\n accessToken: token,\n tokenType: 'Bearer',\n expiresIn: 3600,\n idToken: token,\n scope: session.scope || 'openid',\n refreshToken,\n };\n }\n\n public async refreshAccessToken(params: {\n refreshToken: string;\n clientId?: string;\n }): Promise<{\n accessToken: string;\n tokenType: string;\n expiresIn: number;\n refreshToken: string;\n }> {\n if (!this.offlineAccess) {\n throw new InputError('Refresh tokens are not enabled');\n }\n\n const { accessToken, refreshToken } =\n await this.offlineAccess.refreshAccessToken({\n refreshToken: params.refreshToken,\n tokenIssuer: this.tokenIssuer,\n clientId: params.clientId,\n });\n\n return {\n accessToken,\n tokenType: 'Bearer',\n expiresIn: 3600,\n refreshToken,\n };\n }\n\n /**\n * Verifies client credentials against the registered OIDC clients\n */\n public async verifyClientCredentials(\n options: Required<OidcClientCredentials>,\n ): Promise<boolean> {\n const { clientId, clientSecret } = options;\n const client = await this.oidc.getClient({ clientId });\n if (!client?.clientSecret) {\n return false;\n }\n const expected = Buffer.from(client.clientSecret, 'utf8');\n const provided = Buffer.from(clientSecret, 'utf8');\n if (expected.length !== provided.length) {\n return false;\n }\n return crypto.timingSafeEqual(expected, provided);\n }\n\n /**\n * Verifies a client for token revocation. DCR clients are confidential\n * clients and must present a valid client secret, while CIMD clients are\n * public clients that are identified by their client ID alone. The CIMD\n * metadata document is deliberately not fetched here, so that tokens can\n * still be revoked when the document is unreachable or has been removed.\n */\n public async verifyRevocationClient(\n options: OidcClientCredentials,\n ): Promise<boolean> {\n const { clientId, clientSecret } = options;\n\n let cimdUrl: URL | undefined;\n try {\n cimdUrl = validateCimdUrl(clientId);\n } catch {\n // Not a valid CIMD URL, fall through to DCR\n }\n\n if (cimdUrl) {\n const cimd = this.getCimdConfig();\n return (\n cimd.enabled &&\n cimd.allowedClientIdPatterns.some(pattern =>\n matcher.isMatch(clientId, pattern),\n )\n );\n }\n\n if (!clientSecret) {\n return false;\n }\n\n return this.verifyClientCredentials({ clientId, clientSecret });\n }\n\n /**\n * Revoke a refresh token if offline access is enabled\n */\n public async revokeRefreshToken(token: string): Promise<void> {\n if (!this.offlineAccess) {\n return;\n }\n await this.offlineAccess.revokeRefreshToken(token);\n }\n\n private verifyPkce(opts: {\n codeChallenge: string;\n codeVerifier: string;\n method?: string;\n }): boolean {\n if (!opts.method || opts.method === 'plain') {\n return opts.codeChallenge === opts.codeVerifier;\n }\n\n if (opts.method === 'S256') {\n const hash = crypto\n .createHash('sha256')\n .update(opts.codeVerifier)\n .digest('base64url');\n return opts.codeChallenge === hash;\n }\n\n return false;\n }\n}\n"],"names":["matcher","InputError","decodeJwt","crypto","DateTime","validateCimdUrl","fetchCimdMetadata","NotFoundError","AuthenticationError"],"mappings":";;;;;;;;;;;;;;AAmCA,SAAS,mBAAA,CACP,aACA,eAAA,EACM;AACN,EAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,WAAW,CAAA;AAClC,EAAA,MAAM,UAAA,GAAa,GAAG,MAAA,CAAO,QAAQ,KAAK,MAAA,CAAO,IAAI,CAAA,EAAG,MAAA,CAAO,QAAQ,CAAA,CAAA;AAEvE,EAAA,IAAI,CAAC,gBAAgB,IAAA,CAAK,CAAA,OAAA,KAAWA,yBAAQ,OAAA,CAAQ,UAAA,EAAY,OAAO,CAAC,CAAA,EAAG;AAC1E,IAAA,MAAM,IAAIC,iBAAA,CAAW,CAAA,sBAAA,EAAyB,UAAU,CAAA,CAAA,CAAG,CAAA;AAAA,EAC7D;AACF;AAEA,MAAM,cAAA,GAAiB,CAAC,WAAA,EAAa,WAAA,EAAa,OAAO,CAAA;AACzD,MAAM,0BAAA,GAA6B;AAAA,EACjC,oBAAA;AAAA,EACA,oBAAA;AAAA,EACA,oBAAA;AAAA,EACA,oBAAA;AAAA,EACA,gBAAA;AAAA,EACA;AACF,CAAA;AAOA,SAAS,kBAAA,CACP,YACA,cAAA,EACS;AACT,EAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,UAAU,CAAA;AAEpC,EAAA,IAAI,CAAC,cAAA,CAAe,QAAA,CAAS,SAAA,CAAU,QAAQ,CAAA,EAAG;AAChD,IAAA,OAAO,cAAA,CAAe,SAAS,UAAU,CAAA;AAAA,EAC3C;AAEA,EAAA,OAAO,cAAA,CAAe,KAAK,CAAA,UAAA,KAAc;AACvC,IAAA,IAAI,GAAA;AACJ,IAAA,IAAI;AACF,MAAA,GAAA,GAAM,IAAI,IAAI,UAAU,CAAA;AAAA,IAC1B,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,KAAA;AAAA,IACT;AACA,IAAA,OACE,cAAA,CAAe,QAAA,CAAS,GAAA,CAAI,QAAQ,KACpC,GAAA,CAAI,QAAA,KAAa,SAAA,CAAU,QAAA,IAC3B,IAAI,QAAA,KAAa,SAAA,CAAU,QAAA,IAC3B,GAAA,CAAI,aAAa,SAAA,CAAU,QAAA;AAAA,EAE/B,CAAC,CAAA;AACH;AAYO,MAAM,WAAA,CAAY;AAAA,EACN,IAAA;AAAA,EACA,WAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,IAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,aAAA;AAAA,EAET,WAAA,CACN,MACA,WAAA,EACA,OAAA,EACA,UACA,IAAA,EACA,MAAA,EACA,QACA,aAAA,EACA;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,WAAA,GAAc,WAAA;AACnB,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AACf,IAAA,IAAA,CAAK,QAAA,GAAW,QAAA;AAChB,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,aAAA,GAAgB,aAAA;AAAA,EACvB;AAAA,EAEA,OAAO,OAAO,OAAA,EASX;AACD,IAAA,OAAO,IAAI,WAAA;AAAA,MACT,OAAA,CAAQ,IAAA;AAAA,MACR,OAAA,CAAQ,WAAA;AAAA,MACR,OAAA,CAAQ,OAAA;AAAA,MACR,OAAA,CAAQ,QAAA;AAAA,MACR,OAAA,CAAQ,IAAA;AAAA,MACR,OAAA,CAAQ,MAAA;AAAA,MACR,OAAA,CAAQ,MAAA;AAAA,MACR,OAAA,CAAQ;AAAA,KACV;AAAA,EACF;AAAA,EAEO,gBAAA,GAAmB;AACxB,IAAA,MAAM,UAAA,GAAa,KAAK,MAAA,CAAO,kBAAA;AAAA,MAC7B;AAAA,KACF;AACA,IAAA,MAAM,EAAE,OAAA,EAAS,WAAA,EAAY,GAAI,KAAK,aAAA,EAAc;AAEpD,IAAA,OAAO;AAAA,MACL,QAAQ,IAAA,CAAK,OAAA;AAAA,MACb,cAAA,EAAgB,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,SAAA,CAAA;AAAA,MAC/B,iBAAA,EAAmB,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,YAAA,CAAA;AAAA,MAClC,QAAA,EAAU,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,sBAAA,CAAA;AAAA,MACzB,wBAAA,EAA0B,CAAC,MAAA,EAAQ,UAAU,CAAA;AAAA,MAC7C,uBAAA,EAAyB,CAAC,QAAQ,CAAA;AAAA,MAClC,qCAAA,EAAuC;AAAA,QACrC,OAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAA;AAAA,QACA;AAAA,OACF;AAAA,MACA,gBAAA,EAAkB;AAAA,QAChB,QAAA;AAAA,QACA,GAAI,IAAA,CAAK,aAAA,GAAgB,CAAC,gBAAgB,IAAI;AAAC,OACjD;AAAA,MACA,qCAAA,EAAuC;AAAA,QACrC,qBAAA;AAAA,QACA,oBAAA;AAAA,QACA,GAAI,WAAA,GAAc,CAAC,MAAM,IAAI;AAAC,OAChC;AAAA,MACA,gBAAA,EAAkB,CAAC,KAAA,EAAO,KAAK,CAAA;AAAA,MAC/B,qBAAA,EAAuB;AAAA,QACrB,oBAAA;AAAA,QACA,GAAI,IAAA,CAAK,aAAA,GAAgB,CAAC,eAAe,IAAI;AAAC,OAChD;AAAA,MACA,sBAAA,EAAwB,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,aAAA,CAAA;AAAA,MACvC,gCAAA,EAAkC,CAAC,MAAA,EAAQ,OAAO,CAAA;AAAA,MAClD,GAAI,UAAA,IAAc;AAAA,QAChB,qBAAA,EAAuB,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,YAAA;AAAA,OACxC;AAAA,MACA,GAAA,CAAK,cAAc,WAAA,KAAgB;AAAA,QACjC,mBAAA,EAAqB,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,UAAA;AAAA,OACtC;AAAA,MACA,GAAI,WAAA,IAAe,EAAE,qCAAA,EAAuC,IAAA;AAAK,KACnE;AAAA,EACF;AAAA,EAEA,MAAa,cAAA,GAAiB;AAC5B,IAAA,OAAO,MAAM,IAAA,CAAK,WAAA,CAAY,cAAA,EAAe;AAAA,EAC/C;AAAA,EAEA,MAAa,WAAA,CAAY,EAAE,KAAA,EAAM,EAAsB;AACrD,IAAA,MAAM,WAAA,GAAc,MAAM,IAAA,CAAK,IAAA,CAAK,aAAa,KAAA,EAAO;AAAA,MACtD,kBAAA,EAAoB;AAAA,KACrB,CAAA;AACD,IAAA,IAAI,CAAC,IAAA,CAAK,IAAA,CAAK,WAAA,CAAY,WAAA,EAAa,MAAM,CAAA,EAAG;AAC/C,MAAA,MAAM,IAAIA,iBAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAEA,IAAA,MAAM,EAAE,GAAA,EAAK,aAAA,EAAc,GAAIC,eAAU,KAAK,CAAA;AAE9C,IAAA,IAAI,OAAO,kBAAkB,QAAA,EAAU;AACrC,MAAA,MAAM,IAAI,MAAM,sDAAsD,CAAA;AAAA,IACxE;AACA,IAAA,OAAO,MAAM,IAAA,CAAK,QAAA,CAAS,WAAA,CAAY,aAAa,CAAA;AAAA,EACtD;AAAA,EAEA,MAAa,eAAe,IAAA,EAMzB;AACD,IAAA,MAAM,iBAAA,GAAoBC,wBAAO,UAAA,EAAW;AAC5C,IAAA,MAAM,qBAAA,GAAwBA,wBAAO,UAAA,EAAW;AAEhD,IAAA,MAAM,0BAAA,GAA6B,KAAK,MAAA,CAAO,sBAAA;AAAA,MAC7C;AAAA,KACF,IAAK;AAAA,MACH,YAAA;AAAA,MACA,0BAAA;AAAA,MACA,GAAG;AAAA,KACL;AAEA,IAAA,KAAA,MAAW,WAAA,IAAe,IAAA,CAAK,YAAA,IAAgB,EAAC,EAAG;AACjD,MAAA,mBAAA,CAAoB,aAAa,0BAA0B,CAAA;AAAA,IAC7D;AAEA,IAAA,OAAO,MAAM,IAAA,CAAK,IAAA,CAAK,YAAA,CAAa;AAAA,MAClC,QAAA,EAAU,iBAAA;AAAA,MACV,YAAY,IAAA,CAAK,UAAA;AAAA,MACjB,YAAA,EAAc,qBAAA;AAAA,MACd,YAAA,EAAc,IAAA,CAAK,YAAA,IAAgB,EAAC;AAAA,MACpC,aAAA,EAAe,IAAA,CAAK,aAAA,IAAiB,CAAC,MAAM,CAAA;AAAA,MAC5C,UAAA,EAAY,IAAA,CAAK,UAAA,IAAc,CAAC,oBAAoB,CAAA;AAAA,MACpD,OAAO,IAAA,CAAK;AAAA,KACb,CAAA;AAAA,EACH;AAAA,EAEA,MAAa,2BAA2B,IAAA,EASrC;AACD,IAAA,MAAM;AAAA,MACJ,QAAA;AAAA,MACA,WAAA;AAAA,MACA,YAAA;AAAA,MACA,KAAA;AAAA,MACA,KAAA;AAAA,MACA,KAAA;AAAA,MACA,aAAA;AAAA,MACA;AAAA,KACF,GAAI,IAAA;AAEJ,IAAA,IAAI,iBAAiB,MAAA,EAAQ;AAC3B,MAAA,MAAM,IAAIF,kBAAW,2CAA2C,CAAA;AAAA,IAClE;AAEA,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,cAAc,EAAE,QAAA,EAAU,aAAa,CAAA;AAEjE,IAAA,IAAI,MAAA,CAAO,YAAA,IAAgB,CAAC,aAAA,EAAe;AACzC,MAAA,MAAM,IAAIA,iBAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAEA,IAAA,IAAI,aAAA,EAAe;AACjB,MAAA,IACE,CAAC,uBACD,CAAC,CAAC,QAAQ,OAAO,CAAA,CAAE,QAAA,CAAS,mBAAmB,CAAA,EAC/C;AACA,QAAA,MAAM,IAAIA,kBAAW,+BAA+B,CAAA;AAAA,MACtD;AAAA,IACF;AAEA,IAAA,MAAM,SAAA,GAAYE,wBAAO,UAAA,EAAW;AACpC,IAAA,MAAM,gBAAA,GAAmBC,cAAA,CAAS,GAAA,EAAI,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,CAAA,EAAG,CAAA,CAAE,QAAA,EAAS;AAEpE,IAAA,MAAM,IAAA,CAAK,KAAK,0BAAA,CAA2B;AAAA,MACzC,EAAA,EAAI,SAAA;AAAA,MACJ,QAAA;AAAA,MACA,WAAA;AAAA,MACA,YAAA;AAAA,MACA,KAAA;AAAA,MACA,KAAA;AAAA,MACA,aAAA;AAAA,MACA,mBAAA;AAAA,MACA,KAAA;AAAA,MACA,SAAA,EAAW;AAAA,KACZ,CAAA;AAED,IAAA,OAAO;AAAA,MACL,EAAA,EAAI,SAAA;AAAA,MACJ,YAAY,MAAA,CAAO,UAAA;AAAA,MACnB,KAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAAA,EAEQ,aAAA,GAAgB;AACtB,IAAA,MAAM,OAAA,GACJ,KAAK,MAAA,CAAO,kBAAA;AAAA,MACV;AAAA,KACF,IAAK,KAAA;AAEP,IAAA,MAAM,WAAA,GAAc,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,kCAAA,CAAA;AAEnC,IAAA,OAAO;AAAA,MACL,OAAA;AAAA,MACA,uBAAA,EAAyB,KAAK,MAAA,CAAO,sBAAA;AAAA,QACnC;AAAA,OACF,IAAK,CAAC,qBAAA,EAAuB,sBAAA,EAAwB,WAAW,CAAA;AAAA,MAChE,0BAAA,EACE,KAAK,MAAA,CAAO,sBAAA;AAAA,QACV;AAAA,OACF,IAAK;AAAA,KACT;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,IAAA,EAGzB;AACD,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI;AACF,MAAA,OAAA,GAAUC,0BAAA,CAAgB,KAAK,QAAQ,CAAA;AAAA,IACzC,CAAA,CAAA,MAAQ;AAAA,IAER;AAEA,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,OAAO,KAAK,iBAAA,CAAkB,EAAE,GAAG,IAAA,EAAM,SAAS,CAAA;AAAA,IACpD;AACA,IAAA,OAAO,IAAA,CAAK,iBAAiB,IAAI,CAAA;AAAA,EACnC;AAAA,EAEA,MAAc,kBAAkB,IAAA,EAI7B;AACD,IAAA,MAAM,IAAA,GAAO,KAAK,aAAA,EAAc;AAEhC,IAAA,IAAI,CAAC,KAAK,OAAA,EAAS;AACjB,MAAA,MAAM,IAAIJ,kBAAW,0CAA0C,CAAA;AAAA,IACjE;AAEA,IAAA,IACE,CAAC,KAAK,uBAAA,CAAwB,IAAA;AAAA,MAAK,CAAA,OAAA,KACjCD,wBAAA,CAAQ,OAAA,CAAQ,IAAA,CAAK,UAAU,OAAO;AAAA,KACxC,EACA;AACA,MAAA,MAAM,IAAIC,iBAAA,CAAW,CAAA,mBAAA,EAAsB,IAAA,CAAK,QAAQ,CAAA,CAAA,CAAG,CAAA;AAAA,IAC7D;AAEA,IAAA,MAAM,UAAA,GAAa,MAAMK,4BAAA,CAAkB;AAAA,MACzC,UAAU,IAAA,CAAK,QAAA;AAAA,MACf,cAAc,IAAA,CAAK;AAAA,KACpB,CAAA;AAED,IAAA,IAAI,KAAK,WAAA,EAAa;AACpB,MAAA,mBAAA,CAAoB,IAAA,CAAK,WAAA,EAAa,IAAA,CAAK,0BAA0B,CAAA;AAErE,MAAA,IAAI,CAAC,kBAAA,CAAmB,IAAA,CAAK,WAAA,EAAa,UAAA,CAAW,YAAY,CAAA,EAAG;AAClE,QAAA,MAAM,IAAIL,iBAAA;AAAA,UACR,CAAA,sBAAA,EAAyB,KAAK,WAAW,CAAA,oCAAA;AAAA,SAC3C;AAAA,MACF;AAAA,IACF;AAEA,IAAA,OAAO;AAAA,MACL,YAAY,UAAA,CAAW,UAAA;AAAA,MACvB,cAAc,UAAA,CAAW,YAAA;AAAA,MACzB,YAAA,EAAc;AAAA,KAChB;AAAA,EACF;AAAA,EAEA,MAAc,iBAAiB,IAAA,EAG5B;AACD,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,UAAU,EAAE,QAAA,EAAU,IAAA,CAAK,QAAA,EAAU,CAAA;AACpE,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAIA,kBAAW,mBAAmB,CAAA;AAAA,IAC1C;AAEA,IAAA,IAAI,IAAA,CAAK,eAAe,CAAC,MAAA,CAAO,aAAa,QAAA,CAAS,IAAA,CAAK,WAAW,CAAA,EAAG;AACvE,MAAA,MAAM,IAAIA,iBAAA,CAAW,CAAA,sBAAA,EAAyB,IAAA,CAAK,WAAW,CAAA,CAAA,CAAG,CAAA;AAAA,IACnE;AAEA,IAAA,OAAO;AAAA,MACL,YAAY,MAAA,CAAO,UAAA;AAAA,MACnB,cAAc,MAAA,CAAO,YAAA;AAAA,MACrB,YAAA,EAAc;AAAA,KAChB;AAAA,EACF;AAAA,EAEA,MAAc,uBAAuB,SAAA,EAAmB;AACtD,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,IAAA,CAAK,wBAAwB,EAAE,EAAA,EAAI,WAAW,CAAA;AAEzE,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,MAAM,IAAIM,qBAAc,+BAA+B,CAAA;AAAA,IACzD;AAEA,IAAA,IAAIH,eAAS,UAAA,CAAW,OAAA,CAAQ,SAAS,CAAA,GAAIA,cAAA,CAAS,KAAI,EAAG;AAC3D,MAAA,MAAM,IAAIH,kBAAW,+BAA+B,CAAA;AAAA,IACtD;AAEA,IAAA,IAAI,OAAA,CAAQ,WAAW,SAAA,EAAW;AAChC,MAAA,MAAM,IAAIM,qBAAc,4CAA4C,CAAA;AAAA,IACtE;AAEA,IAAA,OAAO,OAAA;AAAA,EACT;AAAA,EAEA,MAAa,4BAA4B,IAAA,EAGtC;AACD,IAAA,MAAM,EAAE,SAAA,EAAW,aAAA,EAAc,GAAI,IAAA;AACrC,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,sBAAA,CAAuB,SAAS,CAAA;AAE3D,IAAA,MAAM,IAAA,CAAK,KAAK,0BAAA,CAA2B;AAAA,MACzC,IAAI,OAAA,CAAQ,EAAA;AAAA,MACZ,aAAA;AAAA,MACA,MAAA,EAAQ;AAAA,KACT,CAAA;AAED,IAAA,MAAM,oBAAoBJ,uBAAA,CAAO,WAAA,CAAY,EAAE,CAAA,CAAE,SAAS,WAAW,CAAA;AACrE,IAAA,MAAM,aAAA,GAAgBC,cAAA,CAAS,GAAA,EAAI,CAAE,IAAA,CAAK,EAAE,OAAA,EAAS,EAAA,EAAI,CAAA,CAAE,QAAA,EAAS;AAEpE,IAAA,MAAM,IAAA,CAAK,KAAK,uBAAA,CAAwB;AAAA,MACtC,IAAA,EAAM,iBAAA;AAAA,MACN,WAAW,OAAA,CAAQ,EAAA;AAAA,MACnB,SAAA,EAAW;AAAA,KACZ,CAAA;AAED,IAAA,MAAM,WAAA,GAAc,IAAI,GAAA,CAAI,OAAA,CAAQ,WAAW,CAAA;AAE/C,IAAA,WAAA,CAAY,YAAA,CAAa,MAAA,CAAO,MAAA,EAAQ,iBAAiB,CAAA;AACzD,IAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,MAAA,WAAA,CAAY,YAAA,CAAa,MAAA,CAAO,OAAA,EAAS,OAAA,CAAQ,KAAK,CAAA;AAAA,IACxD;AAEA,IAAA,OAAO;AAAA,MACL,WAAA,EAAa,YAAY,QAAA;AAAS,KACpC;AAAA,EACF;AAAA,EAEA,MAAa,wBAAwB,IAAA,EAA6B;AAChE,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,sBAAA,CAAuB,KAAK,SAAS,CAAA;AAChE,IAAA,MAAM,EAAE,UAAA,EAAW,GAAI,MAAM,KAAK,aAAA,CAAc;AAAA,MAC9C,UAAU,OAAA,CAAQ;AAAA,KACnB,CAAA;AAED,IAAA,OAAO;AAAA,MACL,IAAI,OAAA,CAAQ,EAAA;AAAA,MACZ,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,UAAA;AAAA,MACA,aAAa,OAAA,CAAQ,WAAA;AAAA,MACrB,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,cAAc,OAAA,CAAQ,YAAA;AAAA,MACtB,eAAe,OAAA,CAAQ,aAAA;AAAA,MACvB,qBAAqB,OAAA,CAAQ,mBAAA;AAAA,MAC7B,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,WAAW,OAAA,CAAQ,SAAA;AAAA,MACnB,QAAQ,OAAA,CAAQ;AAAA,KAClB;AAAA,EACF;AAAA,EAEA,MAAa,2BAA2B,IAAA,EAGrC;AACD,IAAA,MAAM,EAAE,SAAA,EAAW,aAAA,EAAc,GAAI,IAAA;AACrC,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,sBAAA,CAAuB,SAAS,CAAA;AAE3D,IAAA,MAAM,IAAA,CAAK,KAAK,0BAAA,CAA2B;AAAA,MACzC,IAAI,OAAA,CAAQ,EAAA;AAAA,MACZ,MAAA,EAAQ,UAAA;AAAA,MACR;AAAA,KACD,CAAA;AAAA,EACH;AAAA,EAEA,MAAa,qBAAqB,MAAA,EAK/B;AACD,IAAA,MAAM,EAAE,IAAA,EAAM,WAAA,EAAa,YAAA,EAAc,WAAU,GAAI,MAAA;AAEvD,IAAA,IAAI,cAAc,oBAAA,EAAsB;AACtC,MAAA,MAAM,IAAIH,kBAAW,wBAAwB,CAAA;AAAA,IAC/C;AAEA,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,KAAK,oBAAA,CAAqB,EAAE,MAAM,CAAA;AAC9D,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM,IAAIO,2BAAoB,4BAA4B,CAAA;AAAA,IAC5D;AAEA,IAAA,IAAIJ,eAAS,UAAA,CAAW,QAAA,CAAS,SAAS,CAAA,GAAIA,cAAA,CAAS,KAAI,EAAG;AAC5D,MAAA,MAAM,IAAII,2BAAoB,4BAA4B,CAAA;AAAA,IAC5D;AAEA,IAAA,IAAI,SAAS,IAAA,EAAM;AACjB,MAAA,MAAM,IAAIA,2BAAoB,iCAAiC,CAAA;AAAA,IACjE;AAEA,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,IAAA,CAAK,uBAAA,CAAwB;AAAA,MACtD,IAAI,QAAA,CAAS;AAAA,KACd,CAAA;AAED,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,MAAM,IAAID,qBAAc,+BAA+B,CAAA;AAAA,IACzD;AAEA,IAAA,IAAI,OAAA,CAAQ,gBAAgB,WAAA,EAAa;AACvC,MAAA,MAAM,IAAIC,2BAAoB,uBAAuB,CAAA;AAAA,IACvD;AAEA,IAAA,IAAI,OAAA,CAAQ,WAAW,UAAA,EAAY;AACjC,MAAA,MAAM,IAAIA,2BAAoB,4BAA4B,CAAA;AAAA,IAC5D;AAEA,IAAA,IAAI,CAAC,QAAQ,aAAA,EAAe;AAC1B,MAAA,MAAM,IAAIA,2BAAoB,uCAAuC,CAAA;AAAA,IACvE;AAEA,IAAA,IAAI,QAAQ,aAAA,EAAe;AACzB,MAAA,IAAI,CAAC,YAAA,EAAc;AACjB,QAAA,MAAM,IAAIA,2BAAoB,iCAAiC,CAAA;AAAA,MACjE;AAEA,MAAA,IACE,CAAC,KAAK,UAAA,CAAW;AAAA,QACf,eAAe,OAAA,CAAQ,aAAA;AAAA,QACvB,YAAA;AAAA,QACA,QAAQ,OAAA,CAAQ;AAAA,OACjB,CAAA,EACD;AACA,QAAA,MAAM,IAAIA,2BAAoB,uBAAuB,CAAA;AAAA,MACvD;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,CAAK,KAAK,uBAAA,CAAwB;AAAA,MACtC,IAAA;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA;AAED,IAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,IAAA,CAAK,YAAY,UAAA,CAAW;AAAA,MAClD,MAAA,EAAQ;AAAA,QACN,KAAK,OAAA,CAAQ;AAAA;AACf,KACD,CAAA;AAGD,IAAA,IAAI,YAAA;AACJ,IAAA,MAAM,SAAS,OAAA,CAAQ,KAAA,EAAO,KAAA,CAAM,GAAG,KAAK,EAAC;AAC7C,IAAA,IAAI,MAAA,CAAO,QAAA,CAAS,gBAAgB,CAAA,IAAK,KAAK,aAAA,EAAe;AAC3D,MAAA,IAAI;AACF,QAAA,YAAA,GAAe,MAAM,IAAA,CAAK,aAAA,CAAc,iBAAA,CAAkB;AAAA,UACxD,eAAe,OAAA,CAAQ,aAAA;AAAA,UACvB,cAAc,OAAA,CAAQ;AAAA,SACvB,CAAA;AAAA,MACH,SAAS,GAAA,EAAK;AAGZ,QAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,UACV,CAAA,uCAAA,EAA0C,OAAA,CAAQ,aAAa,CAAA,wCAAA,EAA2C,GAAG,CAAA;AAAA,SAC/G;AAAA,MACF;AAAA,IACF;AAEA,IAAA,OAAO;AAAA,MACL,WAAA,EAAa,KAAA;AAAA,MACb,SAAA,EAAW,QAAA;AAAA,MACX,SAAA,EAAW,IAAA;AAAA,MACX,OAAA,EAAS,KAAA;AAAA,MACT,KAAA,EAAO,QAAQ,KAAA,IAAS,QAAA;AAAA,MACxB;AAAA,KACF;AAAA,EACF;AAAA,EAEA,MAAa,mBAAmB,MAAA,EAQ7B;AACD,IAAA,IAAI,CAAC,KAAK,aAAA,EAAe;AACvB,MAAA,MAAM,IAAIP,kBAAW,gCAAgC,CAAA;AAAA,IACvD;AAEA,IAAA,MAAM,EAAE,WAAA,EAAa,YAAA,KACnB,MAAM,IAAA,CAAK,cAAc,kBAAA,CAAmB;AAAA,MAC1C,cAAc,MAAA,CAAO,YAAA;AAAA,MACrB,aAAa,IAAA,CAAK,WAAA;AAAA,MAClB,UAAU,MAAA,CAAO;AAAA,KAClB,CAAA;AAEH,IAAA,OAAO;AAAA,MACL,WAAA;AAAA,MACA,SAAA,EAAW,QAAA;AAAA,MACX,SAAA,EAAW,IAAA;AAAA,MACX;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,wBACX,OAAA,EACkB;AAClB,IAAA,MAAM,EAAE,QAAA,EAAU,YAAA,EAAa,GAAI,OAAA;AACnC,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,KAAK,SAAA,CAAU,EAAE,UAAU,CAAA;AACrD,IAAA,IAAI,CAAC,QAAQ,YAAA,EAAc;AACzB,MAAA,OAAO,KAAA;AAAA,IACT;AACA,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,MAAA,CAAO,cAAc,MAAM,CAAA;AACxD,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,YAAA,EAAc,MAAM,CAAA;AACjD,IAAA,IAAI,QAAA,CAAS,MAAA,KAAW,QAAA,CAAS,MAAA,EAAQ;AACvC,MAAA,OAAO,KAAA;AAAA,IACT;AACA,IAAA,OAAOE,uBAAA,CAAO,eAAA,CAAgB,QAAA,EAAU,QAAQ,CAAA;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAa,uBACX,OAAA,EACkB;AAClB,IAAA,MAAM,EAAE,QAAA,EAAU,YAAA,EAAa,GAAI,OAAA;AAEnC,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI;AACF,MAAA,OAAA,GAAUE,2BAAgB,QAAQ,CAAA;AAAA,IACpC,CAAA,CAAA,MAAQ;AAAA,IAER;AAEA,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,MAAM,IAAA,GAAO,KAAK,aAAA,EAAc;AAChC,MAAA,OACE,IAAA,CAAK,OAAA,IACL,IAAA,CAAK,uBAAA,CAAwB,IAAA;AAAA,QAAK,CAAA,OAAA,KAChCL,wBAAA,CAAQ,OAAA,CAAQ,QAAA,EAAU,OAAO;AAAA,OACnC;AAAA,IAEJ;AAEA,IAAA,IAAI,CAAC,YAAA,EAAc;AACjB,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,OAAO,IAAA,CAAK,uBAAA,CAAwB,EAAE,QAAA,EAAU,cAAc,CAAA;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,mBAAmB,KAAA,EAA8B;AAC5D,IAAA,IAAI,CAAC,KAAK,aAAA,EAAe;AACvB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,IAAA,CAAK,aAAA,CAAc,kBAAA,CAAmB,KAAK,CAAA;AAAA,EACnD;AAAA,EAEQ,WAAW,IAAA,EAIP;AACV,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,IAAU,IAAA,CAAK,WAAW,OAAA,EAAS;AAC3C,MAAA,OAAO,IAAA,CAAK,kBAAkB,IAAA,CAAK,YAAA;AAAA,IACrC;AAEA,IAAA,IAAI,IAAA,CAAK,WAAW,MAAA,EAAQ;AAC1B,MAAA,MAAM,IAAA,GAAOG,uBAAA,CACV,UAAA,CAAW,QAAQ,CAAA,CACnB,OAAO,IAAA,CAAK,YAAY,CAAA,CACxB,MAAA,CAAO,WAAW,CAAA;AACrB,MAAA,OAAO,KAAK,aAAA,KAAkB,IAAA;AAAA,IAChC;AAEA,IAAA,OAAO,KAAA;AAAA,EACT;AACF;;;;"}
|
|
1
|
+
{"version":3,"file":"OidcService.cjs.js","sources":["../../src/service/OidcService.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n AuthService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { TokenIssuer } from '../identity/types';\nimport { UserInfoDatabase } from '../database/UserInfoDatabase';\nimport {\n AuthenticationError,\n InputError,\n NotFoundError,\n} from '@backstage/errors';\nimport { decodeJwt } from 'jose';\nimport crypto from 'node:crypto';\nimport { OidcDatabase } from '../database/OidcDatabase';\nimport { DateTime } from 'luxon';\nimport matcher from 'matcher';\nimport { OfflineAccessService } from './OfflineAccessService';\nimport { validateCimdUrl, fetchCimdMetadata } from './CimdClient';\n\nconst WILDCARD_PORT = /:\\*(?=\\/|$)/;\nconst EXPLICIT_PROTOCOL = /^[a-z][a-z0-9+.-]*:/i;\n\nfunction createUrlPatternMatcher(pattern: string): (url: URL) => boolean {\n if (pattern === '*') {\n return () => true;\n }\n\n if (!EXPLICIT_PROTOCOL.test(pattern)) {\n throw new Error(\n `Invalid URL pattern '${pattern}', an explicit protocol is required`,\n );\n }\n\n // A ':*' port wildcard can only appear in patterns with an authority\n // section; in patterns such as 'cursor:*' the '*' is part of the path\n const hasWildcardPort =\n pattern.includes('://') && WILDCARD_PORT.test(pattern);\n\n let patternUrl: URL;\n try {\n patternUrl = new URL(\n hasWildcardPort ? pattern.replace(WILDCARD_PORT, ':1') : pattern,\n );\n } catch {\n throw new Error(`Invalid URL pattern '${pattern}'`);\n }\n\n const hostnamePattern = patternUrl.hostname || '*';\n // The path is never empty for http(s) URLs, only for non-special\n // schemes such as in 'cursor://*', where any path is matched instead\n const pathPattern = patternUrl.pathname || '*';\n\n return url =>\n url.protocol === patternUrl.protocol &&\n matcher.isMatch(url.hostname, hostnamePattern) &&\n (hasWildcardPort || url.port === patternUrl.port) &&\n matcher.isMatch(url.pathname, pathPattern);\n}\n\nfunction validateRedirectUri(\n redirectUri: string,\n allowedPatterns: string[],\n): void {\n const parsed = new URL(redirectUri);\n const normalized = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;\n\n if (parsed.username || parsed.password) {\n throw new InputError(`Invalid redirect_uri '${normalized}'`);\n }\n\n const allowedMatchers = allowedPatterns.map(createUrlPatternMatcher);\n if (!allowedMatchers.some(matches => matches(parsed))) {\n throw new InputError(`Invalid redirect_uri '${normalized}'`);\n }\n}\n\nconst LOOPBACK_HOSTS = ['localhost', '127.0.0.1', '[::1]'];\nconst LOOPBACK_REDIRECT_PATTERNS = [\n 'http://localhost:*/*',\n 'http://localhost/*',\n 'http://127.0.0.1:*/*',\n 'http://127.0.0.1/*',\n 'http://[::1]:*/*',\n 'http://[::1]/*',\n];\n\n/**\n * RFC 8252 Section 7.3: For loopback redirect URIs, the authorization server\n * MUST allow any port to be specified at the time of the request. This matches\n * redirect URIs by scheme, hostname, and path only, ignoring the port.\n */\nfunction matchesRedirectUri(\n requestUri: string,\n registeredUris: string[],\n): boolean {\n const requested = new URL(requestUri);\n\n if (!LOOPBACK_HOSTS.includes(requested.hostname)) {\n return registeredUris.includes(requestUri);\n }\n\n return registeredUris.some(registered => {\n let reg: URL;\n try {\n reg = new URL(registered);\n } catch {\n return false;\n }\n return (\n LOOPBACK_HOSTS.includes(reg.hostname) &&\n reg.protocol === requested.protocol &&\n reg.hostname === requested.hostname &&\n reg.pathname === requested.pathname\n );\n });\n}\n\n/**\n * Credentials presented by an OAuth client. Confidential clients authenticate\n * with both a client ID and a client secret, while public clients identify\n * themselves with a client ID alone.\n */\nexport type OidcClientCredentials = {\n clientId: string;\n clientSecret?: string;\n};\n\nexport class OidcService {\n private readonly auth: AuthService;\n private readonly tokenIssuer: TokenIssuer;\n private readonly baseUrl: string;\n private readonly userInfo: UserInfoDatabase;\n private readonly oidc: OidcDatabase;\n private readonly config: RootConfigService;\n private readonly logger: LoggerService;\n private readonly offlineAccess?: OfflineAccessService;\n\n private constructor(\n auth: AuthService,\n tokenIssuer: TokenIssuer,\n baseUrl: string,\n userInfo: UserInfoDatabase,\n oidc: OidcDatabase,\n config: RootConfigService,\n logger: LoggerService,\n offlineAccess?: OfflineAccessService,\n ) {\n this.auth = auth;\n this.tokenIssuer = tokenIssuer;\n this.baseUrl = baseUrl;\n this.userInfo = userInfo;\n this.oidc = oidc;\n this.config = config;\n this.logger = logger;\n this.offlineAccess = offlineAccess;\n }\n\n static create(options: {\n auth: AuthService;\n tokenIssuer: TokenIssuer;\n baseUrl: string;\n userInfo: UserInfoDatabase;\n oidc: OidcDatabase;\n config: RootConfigService;\n logger: LoggerService;\n offlineAccess?: OfflineAccessService;\n }) {\n return new OidcService(\n options.auth,\n options.tokenIssuer,\n options.baseUrl,\n options.userInfo,\n options.oidc,\n options.config,\n options.logger,\n options.offlineAccess,\n );\n }\n\n public getConfiguration() {\n const dcrEnabled = this.config.getOptionalBoolean(\n 'auth.experimentalDynamicClientRegistration.enabled',\n );\n const cimdEnabled = this.isCimdEnabled();\n\n return {\n issuer: this.baseUrl,\n token_endpoint: `${this.baseUrl}/v1/token`,\n userinfo_endpoint: `${this.baseUrl}/v1/userinfo`,\n jwks_uri: `${this.baseUrl}/.well-known/jwks.json`,\n response_types_supported: ['code', 'id_token'],\n subject_types_supported: ['public'],\n id_token_signing_alg_values_supported: [\n 'RS256',\n 'RS384',\n 'RS512',\n 'ES256',\n 'ES384',\n 'ES512',\n 'PS256',\n 'PS384',\n 'PS512',\n 'EdDSA',\n ],\n scopes_supported: [\n 'openid',\n ...(this.offlineAccess ? ['offline_access'] : []),\n ],\n token_endpoint_auth_methods_supported: [\n 'client_secret_basic',\n 'client_secret_post',\n ...(cimdEnabled ? ['none'] : []),\n ],\n claims_supported: ['sub', 'ent'],\n grant_types_supported: [\n 'authorization_code',\n ...(this.offlineAccess ? ['refresh_token'] : []),\n ],\n authorization_endpoint: `${this.baseUrl}/v1/authorize`,\n code_challenge_methods_supported: ['S256', 'plain'],\n ...(dcrEnabled && {\n registration_endpoint: `${this.baseUrl}/v1/register`,\n }),\n ...((dcrEnabled || cimdEnabled) && {\n revocation_endpoint: `${this.baseUrl}/v1/revoke`,\n }),\n ...(cimdEnabled && { client_id_metadata_document_supported: true }),\n };\n }\n\n public isCimdEnabled(): boolean {\n return this.getCimdConfig().enabled;\n }\n\n public async listPublicKeys() {\n return await this.tokenIssuer.listPublicKeys();\n }\n\n public async getUserInfo({ token }: { token: string }) {\n const credentials = await this.auth.authenticate(token, {\n allowLimitedAccess: true,\n });\n if (!this.auth.isPrincipal(credentials, 'user')) {\n throw new InputError(\n 'Userinfo endpoint must be called with a token that represents a user principal',\n );\n }\n\n const { sub: userEntityRef } = decodeJwt(token);\n\n if (typeof userEntityRef !== 'string') {\n throw new Error('Invalid user token, user entity ref must be a string');\n }\n return await this.userInfo.getUserInfo(userEntityRef);\n }\n\n public async registerClient(opts: {\n responseTypes?: string[];\n grantTypes?: string[];\n clientName: string;\n redirectUris?: string[];\n scope?: string;\n }) {\n const generatedClientId = crypto.randomUUID();\n const generatedClientSecret = crypto.randomUUID();\n\n const allowedRedirectUriPatterns = this.config.getOptionalStringArray(\n 'auth.experimentalDynamicClientRegistration.allowedRedirectUriPatterns',\n ) ?? [\n 'cursor://*',\n 'https://www.cursor.com/*',\n ...LOOPBACK_REDIRECT_PATTERNS,\n ];\n\n for (const redirectUri of opts.redirectUris ?? []) {\n validateRedirectUri(redirectUri, allowedRedirectUriPatterns);\n }\n\n return await this.oidc.createClient({\n clientId: generatedClientId,\n clientName: opts.clientName,\n clientSecret: generatedClientSecret,\n redirectUris: opts.redirectUris ?? [],\n responseTypes: opts.responseTypes ?? ['code'],\n grantTypes: opts.grantTypes ?? ['authorization_code'],\n scope: opts.scope,\n });\n }\n\n public async createAuthorizationSession(opts: {\n clientId: string;\n redirectUri: string;\n responseType: string;\n scope?: string;\n state?: string;\n nonce?: string;\n codeChallenge?: string;\n codeChallengeMethod?: string;\n }) {\n const {\n clientId,\n redirectUri,\n responseType,\n scope,\n state,\n nonce,\n codeChallenge,\n codeChallengeMethod,\n } = opts;\n\n if (responseType !== 'code') {\n throw new InputError('Only authorization code flow is supported');\n }\n\n const client = await this.resolveClient({ clientId, redirectUri });\n\n if (client.requiresPkce && !codeChallenge) {\n throw new InputError(\n 'PKCE is required for public clients. Provide a code_challenge parameter.',\n );\n }\n\n if (codeChallenge) {\n if (\n !codeChallengeMethod ||\n !['S256', 'plain'].includes(codeChallengeMethod)\n ) {\n throw new InputError('Invalid code_challenge_method');\n }\n }\n\n const sessionId = crypto.randomUUID();\n const sessionExpiresAt = DateTime.now().plus({ hours: 1 }).toJSDate();\n\n await this.oidc.createAuthorizationSession({\n id: sessionId,\n clientId,\n redirectUri,\n responseType,\n scope,\n state,\n codeChallenge,\n codeChallengeMethod,\n nonce,\n expiresAt: sessionExpiresAt,\n });\n\n return {\n id: sessionId,\n clientName: client.clientName,\n scope,\n redirectUri,\n };\n }\n\n private getCimdConfig() {\n const configPath = this.config.has('auth.clientIdMetadataDocuments')\n ? 'auth.clientIdMetadataDocuments'\n : 'auth.experimentalClientIdMetadataDocuments';\n const enabled =\n this.config.getOptionalBoolean(`${configPath}.enabled`) ?? false;\n\n const cliClientId = `${this.baseUrl}/.well-known/oauth-client/cli.json`;\n\n return {\n enabled,\n allowedClientIdPatterns: this.config.getOptionalStringArray(\n `${configPath}.allowedClientIdPatterns`,\n ) ?? ['https://claude.ai/*', 'https://vscode.dev/*', cliClientId],\n allowedRedirectUriPatterns:\n this.config.getOptionalStringArray(\n `${configPath}.allowedRedirectUriPatterns`,\n ) ?? LOOPBACK_REDIRECT_PATTERNS,\n };\n }\n\n private async resolveClient(opts: {\n clientId: string;\n redirectUri?: string;\n }) {\n let cimdUrl: URL | undefined;\n try {\n cimdUrl = validateCimdUrl(opts.clientId);\n } catch {\n // Not a valid CIMD URL, fall through to DCR\n }\n\n if (cimdUrl) {\n return this.resolveCimdClient({ ...opts, cimdUrl });\n }\n return this.resolveDcrClient(opts);\n }\n\n private async resolveCimdClient(opts: {\n clientId: string;\n cimdUrl: URL;\n redirectUri?: string;\n }) {\n const cimd = this.getCimdConfig();\n\n if (!cimd.enabled) {\n throw new InputError('Client ID metadata documents not enabled');\n }\n\n const clientIdMatchers = cimd.allowedClientIdPatterns.map(\n createUrlPatternMatcher,\n );\n if (!clientIdMatchers.some(matches => matches(opts.cimdUrl))) {\n throw new InputError(`Invalid client_id '${opts.clientId}'`);\n }\n\n const cimdClient = await fetchCimdMetadata({\n clientId: opts.clientId,\n validatedUrl: opts.cimdUrl,\n });\n\n if (opts.redirectUri) {\n validateRedirectUri(opts.redirectUri, cimd.allowedRedirectUriPatterns);\n\n if (!matchesRedirectUri(opts.redirectUri, cimdClient.redirectUris)) {\n throw new InputError(\n `Invalid redirect_uri '${opts.redirectUri}', not registered in client metadata`,\n );\n }\n }\n\n return {\n clientName: cimdClient.clientName,\n redirectUris: cimdClient.redirectUris,\n requiresPkce: true,\n };\n }\n\n private async resolveDcrClient(opts: {\n clientId: string;\n redirectUri?: string;\n }) {\n const client = await this.oidc.getClient({ clientId: opts.clientId });\n if (!client) {\n throw new InputError('Invalid client_id');\n }\n\n if (opts.redirectUri && !client.redirectUris.includes(opts.redirectUri)) {\n throw new InputError(`Invalid redirect_uri '${opts.redirectUri}'`);\n }\n\n return {\n clientName: client.clientName,\n redirectUris: client.redirectUris,\n requiresPkce: false,\n };\n }\n\n private async getValidPendingSession(sessionId: string) {\n const session = await this.oidc.getAuthorizationSession({ id: sessionId });\n\n if (!session) {\n throw new NotFoundError('Invalid authorization session');\n }\n\n if (DateTime.fromJSDate(session.expiresAt) < DateTime.now()) {\n throw new InputError('Authorization session expired');\n }\n\n if (session.status !== 'pending') {\n throw new NotFoundError('Authorization session not found or expired');\n }\n\n return session;\n }\n\n public async approveAuthorizationSession(opts: {\n sessionId: string;\n userEntityRef: string;\n }) {\n const { sessionId, userEntityRef } = opts;\n const session = await this.getValidPendingSession(sessionId);\n\n await this.oidc.updateAuthorizationSession({\n id: session.id,\n userEntityRef,\n status: 'approved',\n });\n\n const authorizationCode = crypto.randomBytes(32).toString('base64url');\n const codeExpiresAt = DateTime.now().plus({ minutes: 10 }).toJSDate();\n\n await this.oidc.createAuthorizationCode({\n code: authorizationCode,\n sessionId: session.id,\n expiresAt: codeExpiresAt,\n });\n\n const redirectUrl = new URL(session.redirectUri);\n\n redirectUrl.searchParams.append('code', authorizationCode);\n if (session.state) {\n redirectUrl.searchParams.append('state', session.state);\n }\n\n return {\n redirectUrl: redirectUrl.toString(),\n };\n }\n\n public async getAuthorizationSession(opts: { sessionId: string }) {\n const session = await this.getValidPendingSession(opts.sessionId);\n const { clientName } = await this.resolveClient({\n clientId: session.clientId,\n });\n\n return {\n id: session.id,\n clientId: session.clientId,\n clientName,\n redirectUri: session.redirectUri,\n scope: session.scope,\n state: session.state,\n responseType: session.responseType,\n codeChallenge: session.codeChallenge,\n codeChallengeMethod: session.codeChallengeMethod,\n nonce: session.nonce,\n expiresAt: session.expiresAt,\n status: session.status,\n };\n }\n\n public async rejectAuthorizationSession(opts: {\n sessionId: string;\n userEntityRef: string;\n }) {\n const { sessionId, userEntityRef } = opts;\n const session = await this.getValidPendingSession(sessionId);\n\n await this.oidc.updateAuthorizationSession({\n id: session.id,\n status: 'rejected',\n userEntityRef,\n });\n }\n\n public async exchangeCodeForToken(params: {\n code: string;\n redirectUri: string;\n codeVerifier?: string;\n grantType: string;\n }) {\n const { code, redirectUri, codeVerifier, grantType } = params;\n\n if (grantType !== 'authorization_code') {\n throw new InputError('Unsupported grant type');\n }\n\n const authCode = await this.oidc.getAuthorizationCode({ code });\n if (!authCode) {\n throw new AuthenticationError('Invalid authorization code');\n }\n\n if (DateTime.fromJSDate(authCode.expiresAt) < DateTime.now()) {\n throw new AuthenticationError('Authorization code expired');\n }\n\n if (authCode.used) {\n throw new AuthenticationError('Authorization code already used');\n }\n\n const session = await this.oidc.getAuthorizationSession({\n id: authCode.sessionId,\n });\n\n if (!session) {\n throw new NotFoundError('Invalid authorization session');\n }\n\n if (session.redirectUri !== redirectUri) {\n throw new AuthenticationError('Redirect URI mismatch');\n }\n\n if (session.status !== 'approved') {\n throw new AuthenticationError('Authorization not approved');\n }\n\n if (!session.userEntityRef) {\n throw new AuthenticationError('No user associated with authorization');\n }\n\n if (session.codeChallenge) {\n if (!codeVerifier) {\n throw new AuthenticationError('Code verifier required for PKCE');\n }\n\n if (\n !this.verifyPkce({\n codeChallenge: session.codeChallenge,\n codeVerifier,\n method: session.codeChallengeMethod,\n })\n ) {\n throw new AuthenticationError('Invalid code verifier');\n }\n }\n\n await this.oidc.updateAuthorizationCode({\n code,\n used: true,\n });\n\n const { token } = await this.tokenIssuer.issueToken({\n claims: {\n sub: session.userEntityRef,\n },\n });\n\n // Check if offline_access scope is requested\n let refreshToken: string | undefined;\n const scopes = session.scope?.split(' ') ?? [];\n if (scopes.includes('offline_access') && this.offlineAccess) {\n try {\n refreshToken = await this.offlineAccess.issueRefreshToken({\n userEntityRef: session.userEntityRef,\n oidcClientId: session.clientId,\n });\n } catch (err) {\n // Don't fail the entire token exchange if refresh token issuance fails.\n // The access token is still valid and should be returned.\n this.logger.warn(\n `Failed to issue refresh token for user ${session.userEntityRef}, offline_access will not be available: ${err}`,\n );\n }\n }\n\n return {\n accessToken: token,\n tokenType: 'Bearer',\n expiresIn: 3600,\n idToken: token,\n scope: session.scope || 'openid',\n refreshToken,\n };\n }\n\n public async refreshAccessToken(params: {\n refreshToken: string;\n clientId?: string;\n }): Promise<{\n accessToken: string;\n tokenType: string;\n expiresIn: number;\n refreshToken: string;\n }> {\n if (!this.offlineAccess) {\n throw new InputError('Refresh tokens are not enabled');\n }\n\n const { accessToken, refreshToken } =\n await this.offlineAccess.refreshAccessToken({\n refreshToken: params.refreshToken,\n tokenIssuer: this.tokenIssuer,\n clientId: params.clientId,\n });\n\n return {\n accessToken,\n tokenType: 'Bearer',\n expiresIn: 3600,\n refreshToken,\n };\n }\n\n /**\n * Verifies client credentials against the registered OIDC clients\n */\n public async verifyClientCredentials(\n options: Required<OidcClientCredentials>,\n ): Promise<boolean> {\n const { clientId, clientSecret } = options;\n const client = await this.oidc.getClient({ clientId });\n if (!client?.clientSecret) {\n return false;\n }\n const expected = Buffer.from(client.clientSecret, 'utf8');\n const provided = Buffer.from(clientSecret, 'utf8');\n if (expected.length !== provided.length) {\n return false;\n }\n return crypto.timingSafeEqual(expected, provided);\n }\n\n /**\n * Verifies a client for token revocation. DCR clients are confidential\n * clients and must present a valid client secret, while CIMD clients are\n * public clients that are identified by their client ID alone. The CIMD\n * metadata document is deliberately not fetched here, so that tokens can\n * still be revoked when the document is unreachable or has been removed.\n */\n public async verifyRevocationClient(\n options: OidcClientCredentials,\n ): Promise<boolean> {\n const { clientId, clientSecret } = options;\n\n let cimdUrl: URL | undefined;\n try {\n cimdUrl = validateCimdUrl(clientId);\n } catch {\n // Not a valid CIMD URL, fall through to DCR\n }\n\n if (cimdUrl) {\n const cimd = this.getCimdConfig();\n return (\n cimd.enabled &&\n cimd.allowedClientIdPatterns.some(pattern =>\n matcher.isMatch(clientId, pattern),\n )\n );\n }\n\n if (!clientSecret) {\n return false;\n }\n\n return this.verifyClientCredentials({ clientId, clientSecret });\n }\n\n /**\n * Revoke a refresh token if offline access is enabled\n */\n public async revokeRefreshToken(token: string): Promise<void> {\n if (!this.offlineAccess) {\n return;\n }\n await this.offlineAccess.revokeRefreshToken(token);\n }\n\n private verifyPkce(opts: {\n codeChallenge: string;\n codeVerifier: string;\n method?: string;\n }): boolean {\n if (!opts.method || opts.method === 'plain') {\n return opts.codeChallenge === opts.codeVerifier;\n }\n\n if (opts.method === 'S256') {\n const hash = crypto\n .createHash('sha256')\n .update(opts.codeVerifier)\n .digest('base64url');\n return opts.codeChallenge === hash;\n }\n\n return false;\n }\n}\n"],"names":["matcher","InputError","decodeJwt","crypto","DateTime","validateCimdUrl","fetchCimdMetadata","NotFoundError","AuthenticationError"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-auth-backend",
|
|
3
|
-
"version": "0.29.2
|
|
3
|
+
"version": "0.29.2",
|
|
4
4
|
"description": "A Backstage backend plugin that handles authentication",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "backend-plugin",
|
|
@@ -47,13 +47,13 @@
|
|
|
47
47
|
"test": "backstage-cli package test"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@backstage/backend-plugin-api": "1.9.3
|
|
51
|
-
"@backstage/catalog-model": "1.9.0",
|
|
52
|
-
"@backstage/config": "1.3.8",
|
|
53
|
-
"@backstage/errors": "1.3.1",
|
|
54
|
-
"@backstage/plugin-auth-node": "0.7.3
|
|
55
|
-
"@backstage/plugin-catalog-node": "2.2.3
|
|
56
|
-
"@backstage/types": "1.2.2",
|
|
50
|
+
"@backstage/backend-plugin-api": "^1.9.3",
|
|
51
|
+
"@backstage/catalog-model": "^1.9.0",
|
|
52
|
+
"@backstage/config": "^1.3.8",
|
|
53
|
+
"@backstage/errors": "^1.3.1",
|
|
54
|
+
"@backstage/plugin-auth-node": "^0.7.3",
|
|
55
|
+
"@backstage/plugin-catalog-node": "^2.2.3",
|
|
56
|
+
"@backstage/types": "^1.2.2",
|
|
57
57
|
"@google-cloud/firestore": "^7.0.0",
|
|
58
58
|
"connect-session-knex": "^4.0.0",
|
|
59
59
|
"cookie-parser": "^1.4.5",
|
|
@@ -72,16 +72,16 @@
|
|
|
72
72
|
"zod-validation-error": "^5.0.0"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
|
-
"@backstage/backend-defaults": "0.17.5
|
|
76
|
-
"@backstage/backend-test-utils": "1.11.5
|
|
77
|
-
"@backstage/cli": "0.36.4
|
|
78
|
-
"@backstage/plugin-auth-backend-module-google-provider": "0.3.17
|
|
79
|
-
"@backstage/plugin-auth-backend-module-guest-provider": "0.2.21
|
|
75
|
+
"@backstage/backend-defaults": "^0.17.5",
|
|
76
|
+
"@backstage/backend-test-utils": "^1.11.5",
|
|
77
|
+
"@backstage/cli": "^0.36.4",
|
|
78
|
+
"@backstage/plugin-auth-backend-module-google-provider": "^0.3.17",
|
|
79
|
+
"@backstage/plugin-auth-backend-module-guest-provider": "^0.2.21",
|
|
80
80
|
"@types/cookie-parser": "^1.4.2",
|
|
81
81
|
"@types/express": "^4.17.6",
|
|
82
82
|
"@types/express-session": "^1.17.2",
|
|
83
83
|
"@types/passport": "^1.0.3",
|
|
84
|
-
"msw": "^
|
|
84
|
+
"msw": "^2.0.0",
|
|
85
85
|
"supertest": "^7.0.0"
|
|
86
86
|
},
|
|
87
87
|
"configSchema": "config.schema.json",
|