@backstage/plugin-auth-backend 0.30.0-next.1 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @backstage/plugin-auth-backend
2
2
 
3
+ ## 0.30.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 274acc5: **BREAKING**: The OAuth redirect URI and client ID metadata document allowlists now match patterns against each URL component separately instead of against the full URL string. Wildcards no longer match across the host and path boundary, patterns must include an explicit protocol and are otherwise rejected as invalid configuration instead of being silently ignored, and redirect URIs that contain embedded credentials are always rejected.
8
+
9
+ A wildcard port also no longer implicitly matches every path: a pattern such as `http://localhost:*` now only matches the root path. Use `http://localhost:*/*` to allow any port and any path. The built-in loopback defaults have been updated accordingly, so this only affects explicitly configured patterns.
10
+
11
+ ### Patch Changes
12
+
13
+ - 4e884d2: Skip SSRF protection for CIMD metadata fetches when the `client_id` matches an exact (non-wildcard) entry in `allowedClientIdPatterns`. Exact patterns mean the administrator explicitly listed a specific URL, so the DNS resolution is trusted. Wildcard patterns still enforce the SSRF check to protect against attacker-controlled subdomains resolving to internal addresses.
14
+ - a995892: Allow ChatGPT Codex clients to use Client ID Metadata Documents by default.
15
+ - 827989d: Fixed the built-in CLI client being rejected when `allowedClientIdPatterns` is configured for Client ID Metadata Documents.
16
+ - Updated dependencies
17
+ - @backstage/catalog-model@1.10.0
18
+ - @backstage/backend-plugin-api@1.10.0
19
+ - @backstage/plugin-auth-node@0.7.4
20
+ - @backstage/plugin-catalog-node@2.2.4
21
+
3
22
  ## 0.30.0-next.1
4
23
 
5
24
  ### Patch Changes
@@ -300,7 +300,7 @@
300
300
  "items": {
301
301
  "type": "string"
302
302
  },
303
- "description": "A list of allowed URI patterns for client_id URLs. Uses glob-style pattern matching where `*` matches any characters. Defaults to `['https://claude.ai/*', 'https://vscode.dev/*', '{baseUrl}/.well-known/oauth-client/cli.json']` where `{baseUrl}` is the auth backend's base URL.\n\nSetting this replaces the Claude and VS Code defaults. The built-in CLI client is always allowed, since this backend serves its metadata document itself.",
303
+ "description": "A list of allowed URI patterns for client_id URLs. Uses glob-style pattern matching where `*` matches any characters. Defaults to `['https://claude.ai/*', 'https://vscode.dev/*', 'https://chatgpt.com/oauth/codex/*\\/client.json', '{baseUrl}/.well-known/oauth-client/cli.json']` where `{baseUrl}` is the auth backend's base URL.\n\nSetting this replaces the Claude, VS Code, and ChatGPT Codex defaults. The built-in CLI client is always allowed, since this backend serves its metadata document itself.",
304
304
  "examples": [
305
305
  [
306
306
  "https://example.com/*",
@@ -336,7 +336,7 @@
336
336
  "items": {
337
337
  "type": "string"
338
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.\n\nSetting this replaces the Claude and VS Code defaults. The built-in CLI client is always allowed, since this backend serves its metadata document itself.",
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/*', 'https://chatgpt.com/oauth/codex/*\\/client.json', '{baseUrl}/.well-known/oauth-client/cli.json']` where `{baseUrl}` is the auth backend's base URL.\n\nSetting this replaces the Claude, VS Code, and ChatGPT Codex defaults. The built-in CLI client is always allowed, since this backend serves its metadata document itself.",
340
340
  "examples": [
341
341
  [
342
342
  "https://example.com/*",
@@ -250,7 +250,12 @@ class OidcService {
250
250
  );
251
251
  return {
252
252
  enabled,
253
- allowedClientIdPatterns: configuredClientIdPatterns ? [.../* @__PURE__ */ new Set([...configuredClientIdPatterns, cliClientId])] : ["https://claude.ai/*", "https://vscode.dev/*", cliClientId],
253
+ allowedClientIdPatterns: configuredClientIdPatterns ? [.../* @__PURE__ */ new Set([...configuredClientIdPatterns, cliClientId])] : [
254
+ "https://claude.ai/*",
255
+ "https://vscode.dev/*",
256
+ "https://chatgpt.com/oauth/codex/*/client.json",
257
+ cliClientId
258
+ ],
254
259
  allowedRedirectUriPatterns: this.config.getOptionalStringArray(
255
260
  `${configPath}.allowedRedirectUriPatterns`
256
261
  ) ?? LOOPBACK_REDIRECT_PATTERNS
@@ -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\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 // This backend serves the CLI client metadata document itself at\n // /.well-known/oauth-client/cli.json whenever CIMD is enabled, so its\n // client_id is always accepted. Configuring allowedClientIdPatterns\n // narrows which third-party clients are allowed, it does not turn off the\n // built-in CLI.\n const configuredClientIdPatterns = this.config.getOptionalStringArray(\n `${configPath}.allowedClientIdPatterns`,\n );\n\n return {\n enabled,\n allowedClientIdPatterns: configuredClientIdPatterns\n ? [...new Set([...configuredClientIdPatterns, cliClientId])]\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 matchingPattern = cimd.allowedClientIdPatterns.find(pattern => {\n const matches = createUrlPatternMatcher(pattern);\n return matches(opts.cimdUrl);\n });\n if (!matchingPattern) {\n throw new InputError(`Invalid client_id '${opts.clientId}'`);\n }\n\n // Exact patterns (no wildcards) mean the admin explicitly listed this\n // URL, so we can skip the SSRF check. Wildcard patterns could match\n // attacker-controlled subdomains that resolve to internal IPs.\n const isExactPattern =\n !matchingPattern.includes('*') && !matchingPattern.includes('?');\n\n const cimdClient = await fetchCimdMetadata({\n clientId: opts.clientId,\n validatedUrl: opts.cimdUrl,\n skipSsrfCheck: isExactPattern,\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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"}
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 // This backend serves the CLI client metadata document itself at\n // /.well-known/oauth-client/cli.json whenever CIMD is enabled, so its\n // client_id is always accepted. Configuring allowedClientIdPatterns\n // narrows which third-party clients are allowed, it does not turn off the\n // built-in CLI.\n const configuredClientIdPatterns = this.config.getOptionalStringArray(\n `${configPath}.allowedClientIdPatterns`,\n );\n\n return {\n enabled,\n allowedClientIdPatterns: configuredClientIdPatterns\n ? [...new Set([...configuredClientIdPatterns, cliClientId])]\n : [\n 'https://claude.ai/*',\n 'https://vscode.dev/*',\n 'https://chatgpt.com/oauth/codex/*/client.json',\n cliClientId,\n ],\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 matchingPattern = cimd.allowedClientIdPatterns.find(pattern => {\n const matches = createUrlPatternMatcher(pattern);\n return matches(opts.cimdUrl);\n });\n if (!matchingPattern) {\n throw new InputError(`Invalid client_id '${opts.clientId}'`);\n }\n\n // Exact patterns (no wildcards) mean the admin explicitly listed this\n // URL, so we can skip the SSRF check. Wildcard patterns could match\n // attacker-controlled subdomains that resolve to internal IPs.\n const isExactPattern =\n !matchingPattern.includes('*') && !matchingPattern.includes('?');\n\n const cimdClient = await fetchCimdMetadata({\n clientId: opts.clientId,\n validatedUrl: opts.cimdUrl,\n skipSsrfCheck: isExactPattern,\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.30.0-next.1",
3
+ "version": "0.30.0",
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.10.0-next.1",
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.4-next.0",
55
- "@backstage/plugin-catalog-node": "2.2.4-next.0",
56
- "@backstage/types": "1.2.2",
50
+ "@backstage/backend-plugin-api": "^1.10.0",
51
+ "@backstage/catalog-model": "^1.10.0",
52
+ "@backstage/config": "^1.3.8",
53
+ "@backstage/errors": "^1.3.1",
54
+ "@backstage/plugin-auth-node": "^0.7.4",
55
+ "@backstage/plugin-catalog-node": "^2.2.4",
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,11 +72,11 @@
72
72
  "zod-validation-error": "^5.0.0"
73
73
  },
74
74
  "devDependencies": {
75
- "@backstage/backend-defaults": "0.17.7-next.2",
76
- "@backstage/backend-test-utils": "1.11.6-next.1",
77
- "@backstage/cli": "0.36.5-next.1",
78
- "@backstage/plugin-auth-backend-module-google-provider": "0.3.18-next.0",
79
- "@backstage/plugin-auth-backend-module-guest-provider": "0.2.22-next.0",
75
+ "@backstage/backend-defaults": "^0.17.7",
76
+ "@backstage/backend-test-utils": "^1.11.6",
77
+ "@backstage/cli": "^0.36.5",
78
+ "@backstage/plugin-auth-backend-module-google-provider": "^0.3.18",
79
+ "@backstage/plugin-auth-backend-module-guest-provider": "^0.2.22",
80
80
  "@types/cookie-parser": "^1.4.2",
81
81
  "@types/express": "^4.17.6",
82
82
  "@types/express-session": "^1.17.2",