@backstage/plugin-auth-backend 0.4.6 → 0.4.7

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.4.7
4
+
5
+ ### Patch Changes
6
+
7
+ - 5ee31f860b: Only use settings that have a value when creating a new FirestoreKeyStore instance
8
+ - 3e0e2f09d5: Added forwarding of the `audience` option for the SAML provider, making it possible to enable `audience` verification.
9
+ - Updated dependencies
10
+ - @backstage/backend-common@0.9.9
11
+ - @backstage/test-utils@0.1.21
12
+ - @backstage/catalog-client@0.5.1
13
+
3
14
  ## 0.4.6
4
15
 
5
16
  ### Patch Changes
package/config.d.ts CHANGED
@@ -75,6 +75,7 @@ export interface Config {
75
75
  logoutUrl?: string;
76
76
  issuer: string;
77
77
  cert: string;
78
+ audience?: string;
78
79
  privateKey?: string;
79
80
  authnContext?: string[];
80
81
  identifierFormat?: string;
package/dist/index.cjs.js CHANGED
@@ -30,6 +30,7 @@ var uuid = require('uuid');
30
30
  var luxon = require('luxon');
31
31
  var backendCommon = require('@backstage/backend-common');
32
32
  var firestore = require('@google-cloud/firestore');
33
+ var lodash = require('lodash');
33
34
  var session = require('express-session');
34
35
  var passport = require('passport');
35
36
  var minimatch = require('minimatch');
@@ -2033,6 +2034,7 @@ const createSamlProvider = (_options) => {
2033
2034
  callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,
2034
2035
  entryPoint: config.getString("entryPoint"),
2035
2036
  logoutUrl: config.getOptionalString("logoutUrl"),
2037
+ audience: config.getOptionalString("audience"),
2036
2038
  issuer: config.getString("issuer"),
2037
2039
  cert: config.getString("cert"),
2038
2040
  privateCert: config.getOptionalString("privateKey"),
@@ -2565,7 +2567,7 @@ class KeyStores {
2565
2567
  }
2566
2568
  if (provider === "firestore") {
2567
2569
  const settings = ks == null ? void 0 : ks.getConfig(provider);
2568
- const keyStore = await FirestoreKeyStore.create({
2570
+ const keyStore = await FirestoreKeyStore.create(lodash.pickBy({
2569
2571
  projectId: settings == null ? void 0 : settings.getOptionalString("projectId"),
2570
2572
  keyFilename: settings == null ? void 0 : settings.getOptionalString("keyFilename"),
2571
2573
  host: settings == null ? void 0 : settings.getOptionalString("host"),
@@ -2573,7 +2575,7 @@ class KeyStores {
2573
2575
  ssl: settings == null ? void 0 : settings.getOptionalBoolean("ssl"),
2574
2576
  path: settings == null ? void 0 : settings.getOptionalString("path"),
2575
2577
  timeout: settings == null ? void 0 : settings.getOptionalNumber("timeout")
2576
- });
2578
+ }, (value) => value !== void 0));
2577
2579
  await FirestoreKeyStore.verifyConnection(keyStore, logger);
2578
2580
  return keyStore;
2579
2581
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/lib/passport/PassportStrategyHelper.ts","../src/lib/oauth/helpers.ts","../src/lib/oauth/OAuthEnvironmentHandler.ts","../src/lib/flow/authFlowHelpers.ts","../src/lib/oauth/OAuthAdapter.ts","../src/lib/catalog/CatalogIdentityClient.ts","../src/lib/catalog/helpers.ts","../src/providers/github/provider.ts","../src/providers/gitlab/provider.ts","../src/providers/google/provider.ts","../src/providers/microsoft/provider.ts","../src/providers/oauth2/provider.ts","../src/providers/okta/provider.ts","../src/providers/bitbucket/provider.ts","../src/providers/atlassian/strategy.ts","../src/providers/atlassian/provider.ts","../src/providers/aws-alb/provider.ts","../src/providers/oidc/provider.ts","../src/providers/saml/provider.ts","../src/providers/auth0/strategy.ts","../src/providers/auth0/provider.ts","../src/providers/onelogin/provider.ts","../src/providers/factories.ts","../src/identity/router.ts","../src/identity/IdentityClient.ts","../src/identity/TokenFactory.ts","../src/identity/DatabaseKeyStore.ts","../src/identity/MemoryKeyStore.ts","../src/identity/FirestoreKeyStore.ts","../src/identity/KeyStores.ts","../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport passport from 'passport';\nimport jwtDecoder from 'jwt-decode';\nimport { InternalOAuthError } from 'passport-oauth2';\n\nimport { PassportProfile } from './types';\nimport { ProfileInfo, RedirectInfo } from '../../providers/types';\n\nexport type PassportDoneCallback<Res, Private = never> = (\n err?: Error,\n response?: Res,\n privateInfo?: Private,\n) => void;\n\nexport const makeProfileInfo = (\n profile: PassportProfile,\n idToken?: string,\n): ProfileInfo => {\n let email: string | undefined = undefined;\n if (profile.emails && profile.emails.length > 0) {\n const [firstEmail] = profile.emails;\n email = firstEmail.value;\n }\n\n let picture: string | undefined = undefined;\n if (profile.avatarUrl) {\n picture = profile.avatarUrl;\n } else if (profile.photos && profile.photos.length > 0) {\n const [firstPhoto] = profile.photos;\n picture = firstPhoto.value;\n }\n\n let displayName: string | undefined =\n profile.displayName ?? profile.username ?? profile.id;\n\n if ((!email || !picture || !displayName) && idToken) {\n try {\n const decoded: Record<string, string> = jwtDecoder(idToken);\n if (!email && decoded.email) {\n email = decoded.email;\n }\n if (!picture && decoded.picture) {\n picture = decoded.picture;\n }\n if (!displayName && decoded.name) {\n displayName = decoded.name;\n }\n } catch (e) {\n throw new Error(`Failed to parse id token and get profile info, ${e}`);\n }\n }\n\n return {\n email,\n picture,\n displayName,\n };\n};\n\nexport const executeRedirectStrategy = async (\n req: express.Request,\n providerStrategy: passport.Strategy,\n options: Record<string, string>,\n): Promise<RedirectInfo> => {\n return new Promise(resolve => {\n const strategy = Object.create(providerStrategy);\n strategy.redirect = (url: string, status?: number) => {\n resolve({ url, status: status ?? undefined });\n };\n\n strategy.authenticate(req, { ...options });\n });\n};\n\nexport const executeFrameHandlerStrategy = async <Result, PrivateInfo = never>(\n req: express.Request,\n providerStrategy: passport.Strategy,\n) => {\n return new Promise<{ result: Result; privateInfo: PrivateInfo }>(\n (resolve, reject) => {\n const strategy = Object.create(providerStrategy);\n strategy.success = (result: any, privateInfo: any) => {\n resolve({ result, privateInfo });\n };\n strategy.fail = (\n info: { type: 'success' | 'error'; message?: string },\n // _status: number,\n ) => {\n reject(new Error(`Authentication rejected, ${info.message ?? ''}`));\n };\n strategy.error = (error: InternalOAuthError) => {\n let message = `Authentication failed, ${error.message}`;\n\n if (error.oauthError?.data) {\n try {\n const errorData = JSON.parse(error.oauthError.data);\n\n if (errorData.message) {\n message += ` - ${errorData.message}`;\n }\n } catch (parseError) {\n message += ` - ${error.oauthError}`;\n }\n }\n\n reject(new Error(message));\n };\n strategy.redirect = () => {\n reject(new Error('Unexpected redirect'));\n };\n strategy.authenticate(req, {});\n },\n );\n};\n\ntype RefreshTokenResponse = {\n /**\n * An access token issued for the signed in user.\n */\n accessToken: string;\n /**\n * Optionally, the server can issue a new Refresh Token for the user\n */\n refreshToken?: string;\n params: any;\n};\n\nexport const executeRefreshTokenStrategy = async (\n providerStrategy: passport.Strategy,\n refreshToken: string,\n scope: string,\n): Promise<RefreshTokenResponse> => {\n return new Promise((resolve, reject) => {\n const anyStrategy = providerStrategy as any;\n const OAuth2 = anyStrategy._oauth2.constructor;\n const oauth2 = new OAuth2(\n anyStrategy._oauth2._clientId,\n anyStrategy._oauth2._clientSecret,\n anyStrategy._oauth2._baseSite,\n anyStrategy._oauth2._authorizeUrl,\n anyStrategy._refreshURL || anyStrategy._oauth2._accessTokenUrl,\n anyStrategy._oauth2._customHeaders,\n );\n\n oauth2.getOAuthAccessToken(\n refreshToken,\n {\n scope,\n grant_type: 'refresh_token',\n },\n (\n err: Error | null,\n accessToken: string,\n newRefreshToken: string,\n params: any,\n ) => {\n if (err) {\n reject(new Error(`Failed to refresh access token ${err.toString()}`));\n }\n if (!accessToken) {\n reject(\n new Error(\n `Failed to refresh access token, no access token received`,\n ),\n );\n }\n\n resolve({\n accessToken,\n refreshToken: newRefreshToken,\n params,\n });\n },\n );\n });\n};\n\ntype ProviderStrategy = {\n userProfile(accessToken: string, callback: Function): void;\n};\n\nexport const executeFetchUserProfileStrategy = async (\n providerStrategy: passport.Strategy,\n accessToken: string,\n): Promise<PassportProfile> => {\n return new Promise((resolve, reject) => {\n const anyStrategy = providerStrategy as unknown as ProviderStrategy;\n anyStrategy.userProfile(\n accessToken,\n (error: Error, rawProfile: PassportProfile) => {\n if (error) {\n reject(error);\n } else {\n resolve(rawProfile);\n }\n },\n );\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport { OAuthState } from './types';\nimport pickBy from 'lodash/pickBy';\n\nexport const readState = (stateString: string): OAuthState => {\n const state = Object.fromEntries(\n new URLSearchParams(Buffer.from(stateString, 'hex').toString('utf-8')),\n );\n if (\n !state.nonce ||\n !state.env ||\n state.nonce?.length === 0 ||\n state.env?.length === 0\n ) {\n throw Error(`Invalid state passed via request`);\n }\n\n return state as OAuthState;\n};\n\nexport const encodeState = (state: OAuthState): string => {\n const stateString = new URLSearchParams(\n pickBy<string>(state, value => value !== undefined),\n ).toString();\n\n return Buffer.from(stateString, 'utf-8').toString('hex');\n};\n\nexport const verifyNonce = (req: express.Request, providerId: string) => {\n const cookieNonce = req.cookies[`${providerId}-nonce`];\n const state: OAuthState = readState(req.query.state?.toString() ?? '');\n const stateNonce = state.nonce;\n\n if (!cookieNonce) {\n throw new Error('Auth response is missing cookie nonce');\n }\n if (stateNonce.length === 0) {\n throw new Error('Auth response is missing state nonce');\n }\n if (cookieNonce !== stateNonce) {\n throw new Error('Invalid nonce');\n }\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport { Config } from '@backstage/config';\nimport { InputError } from '@backstage/errors';\nimport { readState } from './helpers';\nimport { AuthProviderRouteHandlers } from '../../providers/types';\n\nexport class OAuthEnvironmentHandler implements AuthProviderRouteHandlers {\n static mapConfig(\n config: Config,\n factoryFunc: (envConfig: Config) => AuthProviderRouteHandlers,\n ) {\n const envs = config.keys();\n const handlers = new Map<string, AuthProviderRouteHandlers>();\n\n for (const env of envs) {\n const envConfig = config.getConfig(env);\n const handler = factoryFunc(envConfig);\n handlers.set(env, handler);\n }\n\n return new OAuthEnvironmentHandler(handlers);\n }\n\n constructor(\n private readonly handlers: Map<string, AuthProviderRouteHandlers>,\n ) {}\n\n async start(req: express.Request, res: express.Response): Promise<void> {\n const provider = this.getProviderForEnv(req, res);\n await provider?.start(req, res);\n }\n\n async frameHandler(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const provider = this.getProviderForEnv(req, res);\n await provider?.frameHandler(req, res);\n }\n\n async refresh(req: express.Request, res: express.Response): Promise<void> {\n const provider = this.getProviderForEnv(req, res);\n await provider?.refresh?.(req, res);\n }\n\n async logout(req: express.Request, res: express.Response): Promise<void> {\n const provider = this.getProviderForEnv(req, res);\n await provider?.logout?.(req, res);\n }\n\n private getRequestFromEnv(req: express.Request): string | undefined {\n const reqEnv = req.query.env?.toString();\n if (reqEnv) {\n return reqEnv;\n }\n const stateParams = req.query.state?.toString();\n if (!stateParams) {\n return undefined;\n }\n const env = readState(stateParams).env;\n return env;\n }\n\n private getProviderForEnv(\n req: express.Request,\n res: express.Response,\n ): AuthProviderRouteHandlers | undefined {\n const env: string | undefined = this.getRequestFromEnv(req);\n\n if (!env) {\n throw new InputError(`Must specify 'env' query to select environment`);\n }\n\n if (!this.handlers.has(env)) {\n res.status(404).send(\n `Missing configuration.\n <br>\n <br>\n For this flow to work you need to supply a valid configuration for the \"${env}\" environment of provider.`,\n );\n return undefined;\n }\n\n return this.handlers.get(env);\n }\n}\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport crypto from 'crypto';\nimport { WebMessageResponse } from './types';\n\nexport const safelyEncodeURIComponent = (value: string) => {\n // Note the g at the end of the regex; all occurrences of single quotes must\n // be replaced, which encodeURIComponent does not do itself by default\n return encodeURIComponent(value).replace(/'/g, '%27');\n};\n\nexport const postMessageResponse = (\n res: express.Response,\n appOrigin: string,\n response: WebMessageResponse,\n) => {\n const jsonData = JSON.stringify(response);\n const base64Data = safelyEncodeURIComponent(jsonData);\n const base64Origin = safelyEncodeURIComponent(appOrigin);\n\n // NOTE: It is absolutely imperative that we use the safe encoder above, to\n // be sure that the js code below does not allow the injection of malicious\n // data.\n\n // TODO: Make target app origin configurable globally\n\n //\n // postMessage fails silently if the targetOrigin is disallowed.\n // So 2 postMessages are sent from the popup to the parent window.\n // First, the origin being used to post the actual authorization response is\n // shared with the parent window with a postMessage with targetOrigin '*'.\n // Second, the actual authorization response is sent with the app origin\n // as the targetOrigin.\n // If the first message was received but the actual auth response was\n // never received, the event listener can conclude that targetOrigin\n // was disallowed, indicating potential misconfiguration.\n //\n const script = `\n var authResponse = decodeURIComponent('${base64Data}');\n var origin = decodeURIComponent('${base64Origin}');\n var originInfo = {'type': 'config_info', 'targetOrigin': origin};\n (window.opener || window.parent).postMessage(originInfo, '*');\n (window.opener || window.parent).postMessage(JSON.parse(authResponse), origin);\n setTimeout(() => {\n window.close();\n }, 100); // same as the interval of the core-app-api lib/loginPopup.ts (to address race conditions)\n `;\n const hash = crypto.createHash('sha256').update(script).digest('base64');\n\n res.setHeader('Content-Type', 'text/html');\n res.setHeader('X-Frame-Options', 'sameorigin');\n res.setHeader('Content-Security-Policy', `script-src 'sha256-${hash}'`);\n res.end(`<html><body><script>${script}</script></body></html>`);\n};\n\nexport const ensuresXRequestedWith = (req: express.Request) => {\n const requiredHeader = req.header('X-Requested-With');\n if (!requiredHeader || requiredHeader !== 'XMLHttpRequest') {\n return false;\n }\n return true;\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport crypto from 'crypto';\nimport { URL } from 'url';\nimport {\n AuthProviderRouteHandlers,\n BackstageIdentity,\n AuthProviderConfig,\n} from '../../providers/types';\nimport { InputError, isError, NotAllowedError } from '@backstage/errors';\nimport { TokenIssuer } from '../../identity/types';\nimport { readState, verifyNonce } from './helpers';\nimport { postMessageResponse, ensuresXRequestedWith } from '../flow';\nimport {\n OAuthHandlers,\n OAuthStartRequest,\n OAuthRefreshRequest,\n OAuthState,\n} from './types';\n\nexport const THOUSAND_DAYS_MS = 1000 * 24 * 60 * 60 * 1000;\nexport const TEN_MINUTES_MS = 600 * 1000;\n\nexport type Options = {\n providerId: string;\n secure: boolean;\n disableRefresh?: boolean;\n persistScopes?: boolean;\n cookieDomain: string;\n cookiePath: string;\n appOrigin: string;\n tokenIssuer: TokenIssuer;\n isOriginAllowed: (origin: string) => boolean;\n};\n\nexport class OAuthAdapter implements AuthProviderRouteHandlers {\n static fromConfig(\n config: AuthProviderConfig,\n handlers: OAuthHandlers,\n options: Pick<\n Options,\n 'providerId' | 'persistScopes' | 'disableRefresh' | 'tokenIssuer'\n >,\n ): OAuthAdapter {\n const { origin: appOrigin } = new URL(config.appUrl);\n const secure = config.baseUrl.startsWith('https://');\n const url = new URL(config.baseUrl);\n const cookiePath = `${url.pathname}/${options.providerId}`;\n return new OAuthAdapter(handlers, {\n ...options,\n appOrigin,\n cookieDomain: url.hostname,\n cookiePath,\n secure,\n isOriginAllowed: config.isOriginAllowed,\n });\n }\n\n constructor(\n private readonly handlers: OAuthHandlers,\n private readonly options: Options,\n ) {}\n\n async start(req: express.Request, res: express.Response): Promise<void> {\n // retrieve scopes from request\n const scope = req.query.scope?.toString() ?? '';\n const env = req.query.env?.toString();\n const origin = req.query.origin?.toString();\n\n if (!env) {\n throw new InputError('No env provided in request query parameters');\n }\n\n if (this.options.persistScopes) {\n this.setScopesCookie(res, scope);\n }\n\n const nonce = crypto.randomBytes(16).toString('base64');\n // set a nonce cookie before redirecting to oauth provider\n this.setNonceCookie(res, nonce);\n\n const state = { nonce, env, origin };\n const forwardReq = Object.assign(req, { scope, state });\n\n const { url, status } = await this.handlers.start(\n forwardReq as OAuthStartRequest,\n );\n\n res.statusCode = status || 302;\n res.setHeader('Location', url);\n res.setHeader('Content-Length', '0');\n res.end();\n }\n\n async frameHandler(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n let appOrigin = this.options.appOrigin;\n\n try {\n const state: OAuthState = readState(req.query.state?.toString() ?? '');\n\n if (state.origin) {\n try {\n appOrigin = new URL(state.origin).origin;\n } catch {\n throw new NotAllowedError('App origin is invalid, failed to parse');\n }\n if (!this.options.isOriginAllowed(appOrigin)) {\n throw new NotAllowedError(`Origin '${appOrigin}' is not allowed`);\n }\n }\n\n // verify nonce cookie and state cookie on callback\n verifyNonce(req, this.options.providerId);\n\n const { response, refreshToken } = await this.handlers.handler(req);\n\n if (this.options.persistScopes) {\n const grantedScopes = this.getScopesFromCookie(\n req,\n this.options.providerId,\n );\n response.providerInfo.scope = grantedScopes;\n }\n\n if (refreshToken && !this.options.disableRefresh) {\n // set new refresh token\n this.setRefreshTokenCookie(res, refreshToken);\n }\n\n await this.populateIdentity(response.backstageIdentity);\n\n // post message back to popup if successful\n return postMessageResponse(res, appOrigin, {\n type: 'authorization_response',\n response,\n });\n } catch (error) {\n const { name, message } = isError(error)\n ? error\n : new Error('Encountered invalid error'); // Being a bit safe and not forwarding the bad value\n // post error message back to popup if failure\n return postMessageResponse(res, appOrigin, {\n type: 'authorization_response',\n error: { name, message },\n });\n }\n }\n\n async logout(req: express.Request, res: express.Response): Promise<void> {\n if (!ensuresXRequestedWith(req)) {\n res.status(401).send('Invalid X-Requested-With header');\n return;\n }\n\n // remove refresh token cookie if it is set\n this.removeRefreshTokenCookie(res);\n\n res.status(200).send('logout!');\n }\n\n async refresh(req: express.Request, res: express.Response): Promise<void> {\n if (!ensuresXRequestedWith(req)) {\n res.status(401).send('Invalid X-Requested-With header');\n return;\n }\n\n if (!this.handlers.refresh || this.options.disableRefresh) {\n res\n .status(400)\n .send(\n `Refresh token not supported for provider: ${this.options.providerId}`,\n );\n return;\n }\n\n try {\n const refreshToken =\n req.cookies[`${this.options.providerId}-refresh-token`];\n\n // throw error if refresh token is missing in the request\n if (!refreshToken) {\n throw new Error('Missing session cookie');\n }\n\n const scope = req.query.scope?.toString() ?? '';\n\n const forwardReq = Object.assign(req, { scope, refreshToken });\n\n // get new access_token\n const response = await this.handlers.refresh(\n forwardReq as OAuthRefreshRequest,\n );\n\n await this.populateIdentity(response.backstageIdentity);\n\n if (\n response.providerInfo.refreshToken &&\n response.providerInfo.refreshToken !== refreshToken\n ) {\n this.setRefreshTokenCookie(res, response.providerInfo.refreshToken);\n }\n\n res.status(200).json(response);\n } catch (error) {\n res.status(401).send(String(error));\n }\n }\n\n /**\n * If the response from the OAuth provider includes a Backstage identity, we\n * make sure it's populated with all the information we can derive from the user ID.\n */\n private async populateIdentity(identity?: BackstageIdentity) {\n if (!identity) {\n return;\n }\n\n if (!identity.idToken) {\n identity.idToken = await this.options.tokenIssuer.issueToken({\n claims: { sub: identity.id },\n });\n }\n }\n\n private setNonceCookie = (res: express.Response, nonce: string) => {\n res.cookie(`${this.options.providerId}-nonce`, nonce, {\n maxAge: TEN_MINUTES_MS,\n secure: this.options.secure,\n sameSite: 'lax',\n domain: this.options.cookieDomain,\n path: `${this.options.cookiePath}/handler`,\n httpOnly: true,\n });\n };\n\n private setScopesCookie = (res: express.Response, scope: string) => {\n res.cookie(`${this.options.providerId}-scope`, scope, {\n maxAge: TEN_MINUTES_MS,\n secure: this.options.secure,\n sameSite: 'lax',\n domain: this.options.cookieDomain,\n path: `${this.options.cookiePath}/handler`,\n httpOnly: true,\n });\n };\n\n private getScopesFromCookie = (req: express.Request, providerId: string) => {\n return req.cookies[`${providerId}-scope`];\n };\n\n private setRefreshTokenCookie = (\n res: express.Response,\n refreshToken: string,\n ) => {\n res.cookie(`${this.options.providerId}-refresh-token`, refreshToken, {\n maxAge: THOUSAND_DAYS_MS,\n secure: this.options.secure,\n sameSite: 'lax',\n domain: this.options.cookieDomain,\n path: this.options.cookiePath,\n httpOnly: true,\n });\n };\n\n private removeRefreshTokenCookie = (res: express.Response) => {\n res.cookie(`${this.options.providerId}-refresh-token`, '', {\n maxAge: 0,\n secure: this.options.secure,\n sameSite: 'lax',\n domain: this.options.cookieDomain,\n path: this.options.cookiePath,\n httpOnly: true,\n });\n };\n}\n","/*\n * Copyright 2020 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 */\n\nimport { Logger } from 'winston';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport {\n EntityName,\n parseEntityRef,\n RELATION_MEMBER_OF,\n stringifyEntityRef,\n UserEntity,\n} from '@backstage/catalog-model';\nimport { TokenIssuer } from '../../identity';\n\ntype UserQuery = {\n annotations: Record<string, string>;\n};\n\ntype MemberClaimQuery = {\n entityRefs: string[];\n logger?: Logger;\n};\n\n/**\n * A catalog client tailored for reading out identity data from the catalog.\n */\nexport class CatalogIdentityClient {\n private readonly catalogApi: CatalogApi;\n private readonly tokenIssuer: TokenIssuer;\n\n constructor(options: { catalogApi: CatalogApi; tokenIssuer: TokenIssuer }) {\n this.catalogApi = options.catalogApi;\n this.tokenIssuer = options.tokenIssuer;\n }\n\n /**\n * Looks up a single user using a query.\n *\n * Throws a NotFoundError or ConflictError if 0 or multiple users are found.\n */\n async findUser(query: UserQuery): Promise<UserEntity> {\n const filter: Record<string, string> = {\n kind: 'user',\n };\n for (const [key, value] of Object.entries(query.annotations)) {\n filter[`metadata.annotations.${key}`] = value;\n }\n\n // TODO(Rugvip): cache the token\n const token = await this.tokenIssuer.issueToken({\n claims: { sub: 'backstage.io/auth-backend' },\n });\n const { items } = await this.catalogApi.getEntities({ filter }, { token });\n\n if (items.length !== 1) {\n if (items.length > 1) {\n throw new ConflictError('User lookup resulted in multiple matches');\n } else {\n throw new NotFoundError('User not found');\n }\n }\n\n return items[0] as UserEntity;\n }\n\n /**\n * Resolve additional entity claims from the catalog, using the passed-in entity names. Designed\n * to be used within a `signInResolver` where additional entity claims might be provided, but\n * group membership and transient group membership lean on imported catalog relations.\n *\n * Returns a superset of the entity names that can be passed directly to `issueToken` as `ent`.\n */\n async resolveCatalogMembership({\n entityRefs,\n logger,\n }: MemberClaimQuery): Promise<string[]> {\n const resolvedEntityRefs = entityRefs\n .map((ref: string) => {\n try {\n const parsedRef = parseEntityRef(ref.toLocaleLowerCase('en-US'), {\n defaultKind: 'user',\n defaultNamespace: 'default',\n });\n return parsedRef;\n } catch {\n logger?.warn(`Failed to parse entityRef from ${ref}, ignoring`);\n return null;\n }\n })\n .filter((ref): ref is EntityName => ref !== null);\n\n const filter = resolvedEntityRefs.map(ref => ({\n kind: ref.kind,\n 'metadata.namespace': ref.namespace,\n 'metadata.name': ref.name,\n }));\n const entities = await this.catalogApi\n .getEntities({ filter })\n .then(r => r.items);\n\n if (entityRefs.length !== entities.length) {\n const foundEntityNames = entities.map(stringifyEntityRef);\n const missingEntityNames = resolvedEntityRefs\n .map(stringifyEntityRef)\n .filter(s => !foundEntityNames.includes(s));\n logger?.debug(`Entities not found for refs ${missingEntityNames.join()}`);\n }\n\n const memberOf = entities.flatMap(\n e =>\n e!.relations\n ?.filter(r => r.type === RELATION_MEMBER_OF)\n .map(r => r.target) ?? [],\n );\n\n const newEntityRefs = [\n ...new Set(resolvedEntityRefs.concat(memberOf).map(stringifyEntityRef)),\n ];\n\n logger?.debug(`Found catalog membership: ${newEntityRefs.join()}`);\n return newEntityRefs;\n }\n}\n","/*\n * Copyright 2021 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 */\n\nimport {\n RELATION_MEMBER_OF,\n stringifyEntityRef,\n UserEntity,\n} from '@backstage/catalog-model';\nimport { TokenParams } from '../../identity';\n\nexport function getEntityClaims(entity: UserEntity): TokenParams['claims'] {\n const userRef = stringifyEntityRef(entity);\n\n const membershipRefs =\n entity.relations\n ?.filter(\n r =>\n r.type === RELATION_MEMBER_OF &&\n r.target.kind.toLocaleLowerCase('en-US') === 'group',\n )\n .map(r => stringifyEntityRef(r.target)) ?? [];\n\n return {\n sub: userRef,\n ent: [userRef, ...membershipRefs],\n };\n}\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport { Logger } from 'winston';\nimport { Profile as PassportProfile } from 'passport';\nimport { Strategy as GithubStrategy } from 'passport-github2';\nimport {\n executeFetchUserProfileStrategy,\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n RedirectInfo,\n AuthProviderFactory,\n AuthHandler,\n SignInResolver,\n StateEncoder,\n} from '../types';\nimport {\n OAuthAdapter,\n OAuthProviderOptions,\n OAuthHandlers,\n OAuthEnvironmentHandler,\n OAuthStartRequest,\n encodeState,\n OAuthRefreshRequest,\n OAuthResponse,\n} from '../../lib/oauth';\nimport { CatalogIdentityClient } from '../../lib/catalog';\nimport { TokenIssuer } from '../../identity';\n\ntype PrivateInfo = {\n refreshToken?: string;\n};\n\nexport type GithubOAuthResult = {\n fullProfile: PassportProfile;\n params: {\n scope: string;\n expires_in?: string;\n refresh_token_expires_in?: string;\n };\n accessToken: string;\n refreshToken?: string;\n};\n\nexport type GithubAuthProviderOptions = OAuthProviderOptions & {\n tokenUrl?: string;\n userProfileUrl?: string;\n authorizationUrl?: string;\n signInResolver?: SignInResolver<GithubOAuthResult>;\n authHandler: AuthHandler<GithubOAuthResult>;\n stateEncoder: StateEncoder;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n logger: Logger;\n};\n\nexport class GithubAuthProvider implements OAuthHandlers {\n private readonly _strategy: GithubStrategy;\n private readonly signInResolver?: SignInResolver<GithubOAuthResult>;\n private readonly authHandler: AuthHandler<GithubOAuthResult>;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n private readonly stateEncoder: StateEncoder;\n\n constructor(options: GithubAuthProviderOptions) {\n this.signInResolver = options.signInResolver;\n this.authHandler = options.authHandler;\n this.stateEncoder = options.stateEncoder;\n this.tokenIssuer = options.tokenIssuer;\n this.catalogIdentityClient = options.catalogIdentityClient;\n this.logger = options.logger;\n this._strategy = new GithubStrategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n tokenURL: options.tokenUrl,\n userProfileURL: options.userProfileUrl,\n authorizationURL: options.authorizationUrl,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: any,\n done: PassportDoneCallback<GithubOAuthResult, PrivateInfo>,\n ) => {\n done(undefined, { fullProfile, params, accessToken }, { refreshToken });\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n scope: req.scope,\n state: (await this.stateEncoder(req)).encodedState,\n });\n }\n\n async handler(req: express.Request) {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n GithubOAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n return {\n response: await this.handleResult(result),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const {\n accessToken,\n refreshToken: newRefreshToken,\n params,\n } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: newRefreshToken,\n });\n }\n\n private async handleResult(result: GithubOAuthResult) {\n const { profile } = await this.authHandler(result);\n\n const expiresInStr = result.params.expires_in;\n const response: OAuthResponse = {\n providerInfo: {\n accessToken: result.accessToken,\n refreshToken: result.refreshToken, // GitHub expires the old refresh token when used\n scope: result.params.scope,\n expiresInSeconds:\n expiresInStr === undefined ? undefined : Number(expiresInStr),\n },\n profile,\n };\n\n if (this.signInResolver) {\n response.backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n }\n\n return response;\n }\n}\n\nexport const githubDefaultSignInResolver: SignInResolver<GithubOAuthResult> =\n async (info, ctx) => {\n const { fullProfile } = info.result;\n\n const userId = fullProfile.username || fullProfile.id;\n\n const token = await ctx.tokenIssuer.issueToken({\n claims: { sub: userId, ent: [`user:default/${userId}`] },\n });\n\n return { id: userId, token };\n };\n\nexport type GithubProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<GithubOAuthResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n signIn?: {\n /**\n * Maps an auth result to a Backstage identity for the user.\n */\n resolver?: SignInResolver<GithubOAuthResult>;\n };\n\n /**\n * The state encoder used to encode the 'state' parameter on the OAuth request.\n *\n * It should return a string that takes the state params (from the request), url encodes the params\n * and finally base64 encodes them.\n *\n * Providing your own stateEncoder will allow you to add addition parameters to the state field.\n *\n * It is typed as follows:\n * export type StateEncoder = (input: OAuthState) => Promise<{encodedState: string}>;\n *\n * Note: the stateEncoder must encode a 'nonce' value and an 'env' value. Without this, the OAuth flow will fail\n * (These two values will be set by the req.state by default)\n *\n * For more information, please see the helper module in ../../oauth/helpers #readState\n */\n stateEncoder?: StateEncoder;\n};\n\nexport const createGithubProvider = (\n options?: GithubProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const enterpriseInstanceUrl = envConfig.getOptionalString(\n 'enterpriseInstanceUrl',\n );\n const customCallbackUrl = envConfig.getOptionalString('callbackUrl');\n const authorizationUrl = enterpriseInstanceUrl\n ? `${enterpriseInstanceUrl}/login/oauth/authorize`\n : undefined;\n const tokenUrl = enterpriseInstanceUrl\n ? `${enterpriseInstanceUrl}/login/oauth/access_token`\n : undefined;\n const userProfileUrl = enterpriseInstanceUrl\n ? `${enterpriseInstanceUrl}/api/v3/user`\n : undefined;\n const callbackUrl =\n customCallbackUrl ||\n `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<GithubOAuthResult> = options?.authHandler\n ? options.authHandler\n : async ({ fullProfile }) => ({\n profile: makeProfileInfo(fullProfile),\n });\n\n const signInResolverFn =\n options?.signIn?.resolver ?? githubDefaultSignInResolver;\n\n const signInResolver: SignInResolver<GithubOAuthResult> = info =>\n signInResolverFn(info, {\n catalogIdentityClient,\n tokenIssuer,\n logger,\n });\n\n const stateEncoder: StateEncoder =\n options?.stateEncoder ??\n (async (req: OAuthStartRequest): Promise<{ encodedState: string }> => {\n return { encodedState: encodeState(req.state) };\n });\n\n const provider = new GithubAuthProvider({\n clientId,\n clientSecret,\n callbackUrl,\n tokenUrl,\n userProfileUrl,\n authorizationUrl,\n signInResolver,\n authHandler,\n tokenIssuer,\n catalogIdentityClient,\n stateEncoder,\n logger,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n persistScopes: true,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport { Strategy as GitlabStrategy } from 'passport-gitlab2';\nimport { Logger } from 'winston';\n\nimport {\n executeRedirectStrategy,\n executeFrameHandlerStrategy,\n executeRefreshTokenStrategy,\n executeFetchUserProfileStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n RedirectInfo,\n AuthProviderFactory,\n SignInResolver,\n AuthHandler,\n} from '../types';\nimport {\n OAuthAdapter,\n OAuthProviderOptions,\n OAuthHandlers,\n OAuthResponse,\n OAuthEnvironmentHandler,\n OAuthStartRequest,\n OAuthRefreshRequest,\n encodeState,\n OAuthResult,\n} from '../../lib/oauth';\nimport { TokenIssuer } from '../../identity';\nimport { CatalogIdentityClient } from '../../lib/catalog';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\nexport type GitlabAuthProviderOptions = OAuthProviderOptions & {\n baseUrl: string;\n signInResolver?: SignInResolver<OAuthResult>;\n authHandler: AuthHandler<OAuthResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n logger: Logger;\n};\n\nexport const gitlabDefaultSignInResolver: SignInResolver<OAuthResult> = async (\n info,\n ctx,\n) => {\n const { profile, result } = info;\n\n let id = result.fullProfile.id;\n\n if (profile.email) {\n id = profile.email.split('@')[0];\n }\n\n const token = await ctx.tokenIssuer.issueToken({\n claims: { sub: id, ent: [`user:default/${id}`] },\n });\n\n return { id, token };\n};\n\nexport const gitlabDefaultAuthHandler: AuthHandler<OAuthResult> = async ({\n fullProfile,\n params,\n}) => ({\n profile: makeProfileInfo(fullProfile, params.id_token),\n});\n\nexport class GitlabAuthProvider implements OAuthHandlers {\n private readonly _strategy: GitlabStrategy;\n private readonly signInResolver?: SignInResolver<OAuthResult>;\n private readonly authHandler: AuthHandler<OAuthResult>;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n\n constructor(options: GitlabAuthProviderOptions) {\n this.catalogIdentityClient = options.catalogIdentityClient;\n this.logger = options.logger;\n this.tokenIssuer = options.tokenIssuer;\n this.authHandler = options.authHandler;\n this.signInResolver = options.signInResolver;\n\n this._strategy = new GitlabStrategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n baseURL: options.baseUrl,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: any,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(\n undefined,\n { fullProfile, params, accessToken },\n {\n refreshToken,\n },\n );\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n scope: req.scope,\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n return {\n response: await this.handleResult(result),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const {\n accessToken,\n refreshToken: newRefreshToken,\n params,\n } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: newRefreshToken,\n });\n }\n\n private async handleResult(result: OAuthResult): Promise<OAuthResponse> {\n const { profile } = await this.authHandler(result);\n\n const response: OAuthResponse = {\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n refreshToken: result.refreshToken, // GitLab expires the old refresh token when used\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n profile,\n };\n\n if (this.signInResolver) {\n response.backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n }\n\n return response;\n }\n}\n\nexport type GitlabProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<OAuthResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n /**\n * Maps an auth result to a Backstage identity for the user.\n *\n * Set to `'email'` to use the default email-based sign in resolver, which will search\n * the catalog for a single user entity that has a matching `microsoft.com/email` annotation.\n */\n signIn?: {\n resolver?: SignInResolver<OAuthResult>;\n };\n};\n\nexport const createGitlabProvider = (\n options?: GitlabProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const audience = envConfig.getOptionalString('audience');\n const baseUrl = audience || 'https://gitlab.com';\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<OAuthResult> =\n options?.authHandler ?? gitlabDefaultAuthHandler;\n\n const signInResolverFn =\n options?.signIn?.resolver ?? gitlabDefaultSignInResolver;\n\n const signInResolver: SignInResolver<OAuthResult> = info =>\n signInResolverFn(info, {\n catalogIdentityClient,\n tokenIssuer,\n logger,\n });\n\n const provider = new GitlabAuthProvider({\n clientId,\n clientSecret,\n callbackUrl,\n baseUrl,\n authHandler,\n signInResolver,\n catalogIdentityClient,\n logger,\n tokenIssuer,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: false,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport passport from 'passport';\nimport { Strategy as GoogleStrategy } from 'passport-google-oauth20';\nimport { TokenIssuer } from '../../identity/types';\nimport { CatalogIdentityClient, getEntityClaims } from '../../lib/catalog';\nimport {\n encodeState,\n OAuthAdapter,\n OAuthEnvironmentHandler,\n OAuthHandlers,\n OAuthProviderOptions,\n OAuthRefreshRequest,\n OAuthResponse,\n OAuthResult,\n OAuthStartRequest,\n} from '../../lib/oauth';\nimport {\n executeFetchUserProfileStrategy,\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n AuthProviderFactory,\n AuthHandler,\n RedirectInfo,\n SignInResolver,\n} from '../types';\nimport { Logger } from 'winston';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\ntype Options = OAuthProviderOptions & {\n signInResolver?: SignInResolver<OAuthResult>;\n authHandler: AuthHandler<OAuthResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n logger: Logger;\n};\n\nexport class GoogleAuthProvider implements OAuthHandlers {\n private readonly _strategy: GoogleStrategy;\n private readonly signInResolver?: SignInResolver<OAuthResult>;\n private readonly authHandler: AuthHandler<OAuthResult>;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n\n constructor(options: Options) {\n this.signInResolver = options.signInResolver;\n this.authHandler = options.authHandler;\n this.tokenIssuer = options.tokenIssuer;\n this.catalogIdentityClient = options.catalogIdentityClient;\n this.logger = options.logger;\n this._strategy = new GoogleStrategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n // We need passReqToCallback set to false to get params, but there's\n // no matching type signature for that, so instead behold this beauty\n passReqToCallback: false as true,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(\n undefined,\n {\n fullProfile,\n params,\n accessToken,\n refreshToken,\n },\n {\n refreshToken,\n },\n );\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n accessType: 'offline',\n prompt: 'consent',\n scope: req.scope,\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n return {\n response: await this.handleResult(result),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const { accessToken, params } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: req.refreshToken,\n });\n }\n\n private async handleResult(result: OAuthResult) {\n const { profile } = await this.authHandler(result);\n\n const response: OAuthResponse = {\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n profile,\n };\n\n if (this.signInResolver) {\n response.backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n }\n\n return response;\n }\n}\n\nexport const googleEmailSignInResolver: SignInResolver<OAuthResult> = async (\n info,\n ctx,\n) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error('Google profile contained no email');\n }\n\n const entity = await ctx.catalogIdentityClient.findUser({\n annotations: {\n 'google.com/email': profile.email,\n },\n });\n\n const claims = getEntityClaims(entity);\n const token = await ctx.tokenIssuer.issueToken({ claims });\n\n return { id: entity.metadata.name, entity, token };\n};\n\nconst googleDefaultSignInResolver: SignInResolver<OAuthResult> = async (\n info,\n ctx,\n) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error('Google profile contained no email');\n }\n\n let userId: string;\n try {\n const entity = await ctx.catalogIdentityClient.findUser({\n annotations: {\n 'google.com/email': profile.email,\n },\n });\n userId = entity.metadata.name;\n } catch (error) {\n ctx.logger.warn(\n `Failed to look up user, ${error}, falling back to allowing login based on email pattern, this will probably break in the future`,\n );\n userId = profile.email.split('@')[0];\n }\n\n const token = await ctx.tokenIssuer.issueToken({\n claims: { sub: userId, ent: [`user:default/${userId}`] },\n });\n\n return { id: userId, token };\n};\n\nexport type GoogleProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<OAuthResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n signIn?: {\n /**\n * Maps an auth result to a Backstage identity for the user.\n */\n resolver?: SignInResolver<OAuthResult>;\n };\n};\n\nexport const createGoogleProvider = (\n options?: GoogleProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<OAuthResult> = options?.authHandler\n ? options.authHandler\n : async ({ fullProfile, params }) => ({\n profile: makeProfileInfo(fullProfile, params.id_token),\n });\n\n const signInResolverFn =\n options?.signIn?.resolver ?? googleDefaultSignInResolver;\n\n const signInResolver: SignInResolver<OAuthResult> = info =>\n signInResolverFn(info, {\n catalogIdentityClient,\n tokenIssuer,\n logger,\n });\n\n const provider = new GoogleAuthProvider({\n clientId,\n clientSecret,\n callbackUrl,\n signInResolver,\n authHandler,\n tokenIssuer,\n catalogIdentityClient,\n logger,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: false,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport passport from 'passport';\nimport { Strategy as MicrosoftStrategy } from 'passport-microsoft';\nimport { TokenIssuer } from '../../identity/types';\nimport { CatalogIdentityClient, getEntityClaims } from '../../lib/catalog';\nimport {\n encodeState,\n OAuthAdapter,\n OAuthEnvironmentHandler,\n OAuthHandlers,\n OAuthProviderOptions,\n OAuthRefreshRequest,\n OAuthResponse,\n OAuthResult,\n OAuthStartRequest,\n} from '../../lib/oauth';\nimport {\n executeFetchUserProfileStrategy,\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n AuthProviderFactory,\n AuthHandler,\n RedirectInfo,\n SignInResolver,\n} from '../types';\nimport { Logger } from 'winston';\nimport got from 'got';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\ntype Options = OAuthProviderOptions & {\n signInResolver?: SignInResolver<OAuthResult>;\n authHandler: AuthHandler<OAuthResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n logger: Logger;\n authorizationUrl?: string;\n tokenUrl?: string;\n};\n\nexport class MicrosoftAuthProvider implements OAuthHandlers {\n private readonly _strategy: MicrosoftStrategy;\n private readonly signInResolver?: SignInResolver<OAuthResult>;\n private readonly authHandler: AuthHandler<OAuthResult>;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n\n constructor(options: Options) {\n this.signInResolver = options.signInResolver;\n this.authHandler = options.authHandler;\n this.tokenIssuer = options.tokenIssuer;\n this.logger = options.logger;\n this.catalogIdentityClient = options.catalogIdentityClient;\n\n this._strategy = new MicrosoftStrategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n authorizationURL: options.authorizationUrl,\n tokenURL: options.tokenUrl,\n passReqToCallback: false as true,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(undefined, { fullProfile, accessToken, params }, { refreshToken });\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n scope: req.scope,\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n return {\n response: await this.handleResult(result),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const { accessToken, params } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: req.refreshToken,\n });\n }\n\n private async handleResult(result: OAuthResult) {\n const photo = await this.getUserPhoto(result.accessToken);\n result.fullProfile.photos = photo ? [{ value: photo }] : undefined;\n\n const { profile } = await this.authHandler(result);\n\n const response: OAuthResponse = {\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n profile,\n };\n\n if (this.signInResolver) {\n response.backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n }\n\n return response;\n }\n\n private getUserPhoto(accessToken: string): Promise<string | undefined> {\n return new Promise(resolve => {\n got\n .get('https://graph.microsoft.com/v1.0/me/photos/48x48/$value', {\n encoding: 'binary',\n responseType: 'buffer',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n })\n .then(photoData => {\n const photoURL = `data:image/jpeg;base64,${Buffer.from(\n photoData.body,\n ).toString('base64')}`;\n resolve(photoURL);\n })\n .catch(error => {\n this.logger.warn(\n `Could not retrieve user profile photo from Microsoft Graph API: ${error}`,\n );\n // User profile photo is optional, ignore errors and resolve undefined\n resolve(undefined);\n });\n });\n }\n}\n\nexport const microsoftEmailSignInResolver: SignInResolver<OAuthResult> = async (\n info,\n ctx,\n) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error('Microsoft profile contained no email');\n }\n\n const entity = await ctx.catalogIdentityClient.findUser({\n annotations: {\n 'microsoft.com/email': profile.email,\n },\n });\n\n const claims = getEntityClaims(entity);\n const token = await ctx.tokenIssuer.issueToken({ claims });\n\n return { id: entity.metadata.name, entity, token };\n};\n\nexport const microsoftDefaultSignInResolver: SignInResolver<OAuthResult> =\n async (info, ctx) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error('Profile contained no email');\n }\n\n const userId = profile.email.split('@')[0];\n\n const token = await ctx.tokenIssuer.issueToken({\n claims: { sub: userId, ent: [`user:default/${userId}`] },\n });\n\n return { id: userId, token };\n };\n\nexport type MicrosoftProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<OAuthResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n signIn?: {\n /**\n * Maps an auth result to a Backstage identity for the user.\n */\n resolver?: SignInResolver<OAuthResult>;\n };\n};\n\nexport const createMicrosoftProvider = (\n options?: MicrosoftProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const tenantId = envConfig.getString('tenantId');\n\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n const authorizationUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`;\n const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<OAuthResult> = options?.authHandler\n ? options.authHandler\n : async ({ fullProfile, params }) => ({\n profile: makeProfileInfo(fullProfile, params.id_token),\n });\n\n const signInResolverFn =\n options?.signIn?.resolver ?? microsoftDefaultSignInResolver;\n\n const signInResolver: SignInResolver<OAuthResult> = info =>\n signInResolverFn(info, {\n catalogIdentityClient,\n tokenIssuer,\n logger,\n });\n\n const provider = new MicrosoftAuthProvider({\n clientId,\n clientSecret,\n callbackUrl,\n authorizationUrl,\n tokenUrl,\n authHandler,\n signInResolver,\n catalogIdentityClient,\n logger,\n tokenIssuer,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: false,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport passport from 'passport';\nimport { Strategy as OAuth2Strategy } from 'passport-oauth2';\nimport {\n encodeState,\n OAuthAdapter,\n OAuthEnvironmentHandler,\n OAuthHandlers,\n OAuthProviderOptions,\n OAuthRefreshRequest,\n OAuthResponse,\n OAuthResult,\n OAuthStartRequest,\n} from '../../lib/oauth';\nimport {\n executeFetchUserProfileStrategy,\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n AuthHandler,\n AuthProviderFactory,\n RedirectInfo,\n SignInResolver,\n} from '../types';\nimport { CatalogIdentityClient } from '../../lib/catalog';\nimport { TokenIssuer } from '../../identity';\nimport { Logger } from 'winston';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\nexport type OAuth2AuthProviderOptions = OAuthProviderOptions & {\n signInResolver?: SignInResolver<OAuthResult>;\n authHandler: AuthHandler<OAuthResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n authorizationUrl: string;\n tokenUrl: string;\n scope?: string;\n logger: Logger;\n};\n\nexport class OAuth2AuthProvider implements OAuthHandlers {\n private readonly _strategy: OAuth2Strategy;\n private readonly signInResolver?: SignInResolver<OAuthResult>;\n private readonly authHandler: AuthHandler<OAuthResult>;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n\n constructor(options: OAuth2AuthProviderOptions) {\n this.signInResolver = options.signInResolver;\n this.authHandler = options.authHandler;\n this.tokenIssuer = options.tokenIssuer;\n this.catalogIdentityClient = options.catalogIdentityClient;\n this.logger = options.logger;\n\n this._strategy = new OAuth2Strategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n authorizationURL: options.authorizationUrl,\n tokenURL: options.tokenUrl,\n passReqToCallback: false as true,\n scope: options.scope,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(\n undefined,\n {\n fullProfile,\n accessToken,\n refreshToken,\n params,\n },\n {\n refreshToken,\n },\n );\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n accessType: 'offline',\n prompt: 'consent',\n scope: req.scope,\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n return {\n response: await this.handleResult(result),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const refreshTokenResponse = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n const {\n accessToken,\n params,\n refreshToken: updatedRefreshToken,\n } = refreshTokenResponse;\n\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: updatedRefreshToken,\n });\n }\n\n private async handleResult(result: OAuthResult) {\n const { profile } = await this.authHandler(result);\n\n const response: OAuthResponse = {\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n refreshToken: result.refreshToken,\n },\n profile,\n };\n\n if (this.signInResolver) {\n response.backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n }\n\n return response;\n }\n}\n\nexport const oAuth2DefaultSignInResolver: SignInResolver<OAuthResult> = async (\n info,\n ctx,\n) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error('Profile contained no email');\n }\n\n const userId = profile.email.split('@')[0];\n\n const token = await ctx.tokenIssuer.issueToken({\n claims: { sub: userId, ent: [`user:default/${userId}`] },\n });\n\n return { id: userId, token };\n};\n\nexport type OAuth2ProviderOptions = {\n authHandler?: AuthHandler<OAuthResult>;\n\n signIn?: {\n resolver?: SignInResolver<OAuthResult>;\n };\n};\n\nexport const createOAuth2Provider = (\n options?: OAuth2ProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n const authorizationUrl = envConfig.getString('authorizationUrl');\n const tokenUrl = envConfig.getString('tokenUrl');\n const scope = envConfig.getOptionalString('scope');\n const disableRefresh =\n envConfig.getOptionalBoolean('disableRefresh') ?? false;\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<OAuthResult> = options?.authHandler\n ? options.authHandler\n : async ({ fullProfile, params }) => ({\n profile: makeProfileInfo(fullProfile, params.id_token),\n });\n\n const signInResolverFn =\n options?.signIn?.resolver ?? oAuth2DefaultSignInResolver;\n\n const signInResolver: SignInResolver<OAuthResult> = info =>\n signInResolverFn(info, {\n catalogIdentityClient,\n tokenIssuer,\n logger,\n });\n\n const provider = new OAuth2AuthProvider({\n clientId,\n clientSecret,\n tokenIssuer,\n catalogIdentityClient,\n callbackUrl,\n signInResolver,\n authHandler,\n authorizationUrl,\n tokenUrl,\n scope,\n logger,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 express from 'express';\nimport {\n OAuthAdapter,\n OAuthProviderOptions,\n OAuthHandlers,\n OAuthResponse,\n OAuthEnvironmentHandler,\n OAuthStartRequest,\n encodeState,\n OAuthRefreshRequest,\n OAuthResult,\n} from '../../lib/oauth';\nimport { Strategy as OktaStrategy } from 'passport-okta-oauth';\nimport passport from 'passport';\nimport {\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n executeFetchUserProfileStrategy,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n AuthProviderFactory,\n AuthHandler,\n RedirectInfo,\n SignInResolver,\n} from '../types';\nimport { StateStore } from 'passport-oauth2';\nimport { CatalogIdentityClient, getEntityClaims } from '../../lib/catalog';\nimport { TokenIssuer } from '../../identity';\nimport { Logger } from 'winston';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\nexport type OktaAuthProviderOptions = OAuthProviderOptions & {\n audience: string;\n signInResolver?: SignInResolver<OAuthResult>;\n authHandler: AuthHandler<OAuthResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n logger: Logger;\n};\n\nexport class OktaAuthProvider implements OAuthHandlers {\n private readonly _strategy: any;\n private readonly _signInResolver?: SignInResolver<OAuthResult>;\n private readonly _authHandler: AuthHandler<OAuthResult>;\n private readonly _tokenIssuer: TokenIssuer;\n private readonly _catalogIdentityClient: CatalogIdentityClient;\n private readonly _logger: Logger;\n\n /**\n * Due to passport-okta-oauth forcing options.state = true,\n * passport-oauth2 requires express-session to be installed\n * so that the 'state' parameter of the oauth2 flow can be stored.\n * This implementation of StateStore matches the NullStore found within\n * passport-oauth2, which is the StateStore implementation used when options.state = false,\n * allowing us to avoid using express-session in order to integrate with Okta.\n */\n private _store: StateStore = {\n store(_req: express.Request, cb: any) {\n cb(null, null);\n },\n verify(_req: express.Request, _state: string, cb: any) {\n cb(null, true);\n },\n };\n\n constructor(options: OktaAuthProviderOptions) {\n this._signInResolver = options.signInResolver;\n this._authHandler = options.authHandler;\n this._tokenIssuer = options.tokenIssuer;\n this._catalogIdentityClient = options.catalogIdentityClient;\n this._logger = options.logger;\n\n this._strategy = new OktaStrategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n audience: options.audience,\n passReqToCallback: false as true,\n store: this._store,\n response_type: 'code',\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(\n undefined,\n {\n accessToken,\n refreshToken,\n params,\n fullProfile,\n },\n {\n refreshToken,\n },\n );\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n accessType: 'offline',\n prompt: 'consent',\n scope: req.scope,\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n return {\n response: await this.handleResult(result),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const { accessToken, params } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: req.refreshToken,\n });\n }\n\n private async handleResult(result: OAuthResult) {\n const { profile } = await this._authHandler(result);\n\n const response: OAuthResponse = {\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n profile,\n };\n\n if (this._signInResolver) {\n response.backstageIdentity = await this._signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this._tokenIssuer,\n catalogIdentityClient: this._catalogIdentityClient,\n logger: this._logger,\n },\n );\n }\n\n return response;\n }\n}\n\nexport const oktaEmailSignInResolver: SignInResolver<OAuthResult> = async (\n info,\n ctx,\n) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error('Okta profile contained no email');\n }\n\n const entity = await ctx.catalogIdentityClient.findUser({\n annotations: {\n 'okta.com/email': profile.email,\n },\n });\n\n const claims = getEntityClaims(entity);\n const token = await ctx.tokenIssuer.issueToken({ claims });\n\n return { id: entity.metadata.name, entity, token };\n};\n\nexport const oktaDefaultSignInResolver: SignInResolver<OAuthResult> = async (\n info,\n ctx,\n) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error('Okta profile contained no email');\n }\n\n // TODO(Rugvip): Hardcoded to the local part of the email for now\n const userId = profile.email.split('@')[0];\n\n const token = await ctx.tokenIssuer.issueToken({\n claims: { sub: userId, ent: [`user:default/${userId}`] },\n });\n\n return { id: userId, token };\n};\n\nexport type OktaProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<OAuthResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n signIn?: {\n /**\n * Maps an auth result to a Backstage identity for the user.\n */\n resolver?: SignInResolver<OAuthResult>;\n };\n};\n\nexport const createOktaProvider = (\n _options?: OktaProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const audience = envConfig.getString('audience');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n // This is a safe assumption as `passport-okta-oauth` uses the audience\n // as the base for building the authorization, token, and user info URLs.\n // https://github.com/fischerdan/passport-okta-oauth/blob/ea9ac42d/lib/passport-okta-oauth/oauth2.js#L12-L14\n if (!audience.startsWith('https://')) {\n throw new Error(\"URL for 'audience' must start with 'https://'.\");\n }\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<OAuthResult> = _options?.authHandler\n ? _options.authHandler\n : async ({ fullProfile, params }) => ({\n profile: makeProfileInfo(fullProfile, params.id_token),\n });\n\n const signInResolverFn =\n _options?.signIn?.resolver ?? oktaDefaultSignInResolver;\n\n const signInResolver: SignInResolver<OAuthResult> = info =>\n signInResolverFn(info, {\n catalogIdentityClient,\n tokenIssuer,\n logger,\n });\n\n const provider = new OktaAuthProvider({\n audience,\n clientId,\n clientSecret,\n callbackUrl,\n authHandler,\n signInResolver,\n tokenIssuer,\n catalogIdentityClient,\n logger,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: false,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport passport, { Profile as PassportProfile } from 'passport';\nimport { Strategy as BitbucketStrategy } from 'passport-bitbucket-oauth2';\nimport { TokenIssuer } from '../../identity/types';\nimport { CatalogIdentityClient, getEntityClaims } from '../../lib/catalog';\nimport {\n encodeState,\n OAuthAdapter,\n OAuthEnvironmentHandler,\n OAuthHandlers,\n OAuthProviderOptions,\n OAuthRefreshRequest,\n OAuthResponse,\n OAuthResult,\n OAuthStartRequest,\n} from '../../lib/oauth';\nimport {\n executeFetchUserProfileStrategy,\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n AuthProviderFactory,\n AuthHandler,\n RedirectInfo,\n SignInResolver,\n} from '../types';\nimport { Logger } from 'winston';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\ntype Options = OAuthProviderOptions & {\n signInResolver?: SignInResolver<OAuthResult>;\n authHandler: AuthHandler<BitbucketOAuthResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n logger: Logger;\n};\n\nexport type BitbucketOAuthResult = {\n fullProfile: BitbucketPassportProfile;\n params: {\n id_token?: string;\n scope: string;\n expires_in: number;\n };\n accessToken: string;\n refreshToken?: string;\n};\n\nexport type BitbucketPassportProfile = PassportProfile & {\n id?: string;\n displayName?: string;\n username?: string;\n avatarUrl?: string;\n _json?: {\n links?: {\n avatar?: {\n href?: string;\n };\n };\n };\n};\n\nexport class BitbucketAuthProvider implements OAuthHandlers {\n private readonly _strategy: BitbucketStrategy;\n private readonly signInResolver?: SignInResolver<OAuthResult>;\n private readonly authHandler: AuthHandler<OAuthResult>;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n\n constructor(options: Options) {\n this.signInResolver = options.signInResolver;\n this.authHandler = options.authHandler;\n this.tokenIssuer = options.tokenIssuer;\n this.catalogIdentityClient = options.catalogIdentityClient;\n this.logger = options.logger;\n this._strategy = new BitbucketStrategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n // We need passReqToCallback set to false to get params, but there's\n // no matching type signature for that, so instead behold this beauty\n passReqToCallback: false as true,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(\n undefined,\n {\n fullProfile,\n params,\n accessToken,\n refreshToken,\n },\n {\n refreshToken,\n },\n );\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n accessType: 'offline',\n prompt: 'consent',\n scope: req.scope,\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n return {\n response: await this.handleResult(result),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const { accessToken, params } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: req.refreshToken,\n });\n }\n\n private async handleResult(result: BitbucketOAuthResult) {\n result.fullProfile.avatarUrl =\n result.fullProfile._json!.links!.avatar!.href;\n const { profile } = await this.authHandler(result);\n\n const response: OAuthResponse = {\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n profile,\n };\n\n if (this.signInResolver) {\n response.backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n }\n\n return response;\n }\n}\n\nexport const bitbucketUsernameSignInResolver: SignInResolver<BitbucketOAuthResult> =\n async (info, ctx) => {\n const { result } = info;\n\n if (!result.fullProfile.username) {\n throw new Error('Bitbucket profile contained no Username');\n }\n\n const entity = await ctx.catalogIdentityClient.findUser({\n annotations: {\n 'bitbucket.org/username': result.fullProfile.username,\n },\n });\n\n const claims = getEntityClaims(entity);\n const token = await ctx.tokenIssuer.issueToken({ claims });\n\n return { id: entity.metadata.name, entity, token };\n };\n\nexport const bitbucketUserIdSignInResolver: SignInResolver<BitbucketOAuthResult> =\n async (info, ctx) => {\n const { result } = info;\n\n if (!result.fullProfile.id) {\n throw new Error('Bitbucket profile contained no User ID');\n }\n\n const entity = await ctx.catalogIdentityClient.findUser({\n annotations: {\n 'bitbucket.org/user-id': result.fullProfile.id,\n },\n });\n\n const claims = getEntityClaims(entity);\n const token = await ctx.tokenIssuer.issueToken({ claims });\n\n return { id: entity.metadata.name, entity, token };\n };\n\nexport type BitbucketProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<OAuthResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n signIn?: {\n /**\n * Maps an auth result to a Backstage identity for the user.\n */\n resolver: SignInResolver<OAuthResult>;\n };\n};\n\nexport const createBitbucketProvider = (\n options?: BitbucketProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<BitbucketOAuthResult> =\n options?.authHandler\n ? options.authHandler\n : async ({ fullProfile, params }) => ({\n profile: makeProfileInfo(fullProfile, params.id_token),\n });\n\n const provider = new BitbucketAuthProvider({\n clientId,\n clientSecret,\n callbackUrl,\n signInResolver: options?.signIn?.resolver,\n authHandler,\n tokenIssuer,\n catalogIdentityClient,\n logger,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: false,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport OAuth2Strategy, { InternalOAuthError } from 'passport-oauth2';\nimport { Profile } from 'passport';\n\ninterface ProfileResponse {\n account_id: string;\n email: string;\n name: string;\n picture: string;\n nickname: string;\n}\n\ninterface AtlassianStrategyOptions {\n clientID: string;\n clientSecret: string;\n callbackURL: string;\n scope: string;\n}\n\nconst defaultScopes = ['offline_access', 'read:me'];\n\nexport default class AtlassianStrategy extends OAuth2Strategy {\n private readonly profileURL: string;\n\n constructor(\n options: AtlassianStrategyOptions,\n verify: OAuth2Strategy.VerifyFunction,\n ) {\n if (!options.scope) {\n throw new TypeError('Atlassian requires a scope option');\n }\n\n const scopes = options.scope.split(' ');\n\n const optionsWithURLs = {\n ...options,\n authorizationURL: `https://auth.atlassian.com/authorize`,\n tokenURL: `https://auth.atlassian.com/oauth/token`,\n scope: Array.from(new Set([...defaultScopes, ...scopes])),\n };\n\n super(optionsWithURLs, verify);\n this.profileURL = 'https://api.atlassian.com/me';\n this.name = 'atlassian';\n\n this._oauth2.useAuthorizationHeaderforGET(true);\n }\n\n authorizationParams() {\n return {\n audience: 'api.atlassian.com',\n prompt: 'consent',\n };\n }\n\n userProfile(\n accessToken: string,\n done: (err?: Error | null, profile?: any) => void,\n ): void {\n this._oauth2.get(this.profileURL, accessToken, (err, body) => {\n if (err) {\n return done(\n new InternalOAuthError(\n 'Failed to fetch user profile',\n err.statusCode,\n ),\n );\n }\n\n if (!body) {\n return done(\n new Error('Failed to fetch user profile, body cannot be empty'),\n );\n }\n\n try {\n const json = typeof body !== 'string' ? body.toString() : body;\n const profile = AtlassianStrategy.parse(json);\n return done(null, profile);\n } catch (e) {\n return done(new Error('Failed to parse user profile'));\n }\n });\n }\n\n static parse(json: string): Profile {\n const resp = JSON.parse(json) as ProfileResponse;\n\n return {\n id: resp.account_id,\n provider: 'atlassian',\n username: resp.nickname,\n displayName: resp.name,\n emails: [{ value: resp.email }],\n photos: [{ value: resp.picture }],\n };\n }\n}\n","/*\n * Copyright 2020 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 */\n\nimport AtlassianStrategy from './strategy';\nimport {\n encodeState,\n OAuthAdapter,\n OAuthEnvironmentHandler,\n OAuthHandlers,\n OAuthProviderOptions,\n OAuthRefreshRequest,\n OAuthResponse,\n OAuthResult,\n OAuthStartRequest,\n} from '../../lib/oauth';\nimport passport from 'passport';\nimport {\n executeFetchUserProfileStrategy,\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n AuthHandler,\n AuthProviderFactory,\n RedirectInfo,\n SignInResolver,\n} from '../types';\nimport express from 'express';\nimport { TokenIssuer } from '../../identity';\nimport { CatalogIdentityClient } from '../../lib/catalog';\nimport { Logger } from 'winston';\n\nexport type AtlassianAuthProviderOptions = OAuthProviderOptions & {\n scopes: string;\n signInResolver?: SignInResolver<OAuthResult>;\n authHandler: AuthHandler<OAuthResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n logger: Logger;\n};\n\nexport const atlassianDefaultAuthHandler: AuthHandler<OAuthResult> = async ({\n fullProfile,\n params,\n}) => ({\n profile: makeProfileInfo(fullProfile, params.id_token),\n});\n\nexport class AtlassianAuthProvider implements OAuthHandlers {\n private readonly _strategy: AtlassianStrategy;\n private readonly signInResolver?: SignInResolver<OAuthResult>;\n private readonly authHandler: AuthHandler<OAuthResult>;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n\n constructor(options: AtlassianAuthProviderOptions) {\n this.catalogIdentityClient = options.catalogIdentityClient;\n this.logger = options.logger;\n this.tokenIssuer = options.tokenIssuer;\n this.authHandler = options.authHandler;\n this.signInResolver = options.signInResolver;\n\n this._strategy = new AtlassianStrategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n scope: options.scopes,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult>,\n ) => {\n done(undefined, {\n fullProfile,\n accessToken,\n refreshToken,\n params,\n });\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result } = await executeFrameHandlerStrategy<OAuthResult>(\n req,\n this._strategy,\n );\n\n return {\n response: await this.handleResult(result),\n refreshToken: result.refreshToken ?? '',\n };\n }\n\n private async handleResult(result: OAuthResult): Promise<OAuthResponse> {\n const { profile } = await this.authHandler(result);\n\n const response: OAuthResponse = {\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n profile,\n };\n\n if (this.signInResolver) {\n response.backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n }\n\n return response;\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const {\n accessToken,\n params,\n refreshToken: newRefreshToken,\n } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: newRefreshToken,\n });\n }\n}\n\nexport type AtlassianProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<OAuthResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n signIn?: {\n resolver: SignInResolver<OAuthResult>;\n };\n};\n\nexport const createAtlassianProvider = (\n options?: AtlassianProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const scopes = envConfig.getString('scopes');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<OAuthResult> =\n options?.authHandler ?? atlassianDefaultAuthHandler;\n\n const provider = new AtlassianAuthProvider({\n clientId,\n clientSecret,\n scopes,\n callbackUrl,\n authHandler,\n signInResolver: options?.signIn?.resolver,\n catalogIdentityClient,\n logger,\n tokenIssuer,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: true,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2021 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 AuthHandler,\n AuthProviderFactory,\n AuthProviderRouteHandlers,\n AuthResponse,\n SignInResolver,\n} from '../types';\nimport express from 'express';\nimport fetch from 'cross-fetch';\nimport * as crypto from 'crypto';\nimport { KeyObject } from 'crypto';\nimport { Logger } from 'winston';\nimport NodeCache from 'node-cache';\nimport { JWT } from 'jose';\nimport { TokenIssuer } from '../../identity/types';\nimport { CatalogIdentityClient } from '../../lib/catalog';\nimport { Profile as PassportProfile } from 'passport';\nimport { makeProfileInfo } from '../../lib/passport';\nimport { AuthenticationError } from '@backstage/errors';\n\nexport const ALB_JWT_HEADER = 'x-amzn-oidc-data';\nexport const ALB_ACCESSTOKEN_HEADER = 'x-amzn-oidc-accesstoken';\n\ntype Options = {\n region: string;\n issuer?: string;\n logger: Logger;\n authHandler: AuthHandler<AwsAlbResult>;\n signInResolver: SignInResolver<AwsAlbResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n};\n\nexport const getJWTHeaders = (input: string): AwsAlbHeaders => {\n const encoded = input.split('.')[0];\n return JSON.parse(Buffer.from(encoded, 'base64').toString('utf8'));\n};\n\nexport type AwsAlbHeaders = {\n alg: string;\n kid: string;\n signer: string;\n iss: string;\n client: string;\n exp: number;\n};\n\nexport type AwsAlbClaims = {\n sub: string;\n name: string;\n family_name: string;\n given_name: string;\n picture: string;\n email: string;\n exp: number;\n iss: string;\n};\n\nexport type AwsAlbResult = {\n fullProfile: PassportProfile;\n expiresInSeconds?: number;\n accessToken: string;\n};\n\nexport type AwsAlbProviderInfo = {\n /**\n * An access token issued for the signed in user.\n */\n accessToken: string;\n /**\n * Expiry of the access token in seconds.\n */\n expiresInSeconds?: number;\n};\n\nexport type AwsAlbResponse = AuthResponse<AwsAlbProviderInfo>;\n\nexport class AwsAlbAuthProvider implements AuthProviderRouteHandlers {\n private readonly region: string;\n private readonly issuer?: string;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n private readonly keyCache: NodeCache;\n private readonly authHandler: AuthHandler<AwsAlbResult>;\n private readonly signInResolver: SignInResolver<AwsAlbResult>;\n\n constructor(options: Options) {\n this.region = options.region;\n this.issuer = options.issuer;\n this.authHandler = options.authHandler;\n this.signInResolver = options.signInResolver;\n this.tokenIssuer = options.tokenIssuer;\n this.catalogIdentityClient = options.catalogIdentityClient;\n this.logger = options.logger;\n this.keyCache = new NodeCache({ stdTTL: 3600 });\n }\n\n frameHandler(): Promise<void> {\n return Promise.resolve(undefined);\n }\n\n async refresh(req: express.Request, res: express.Response): Promise<void> {\n try {\n const result = await this.getResult(req);\n const response = await this.handleResult(result);\n res.json(response);\n } catch (e) {\n this.logger.error('Exception occurred during AWS ALB token refresh', e);\n res.status(401);\n res.end();\n }\n }\n\n start(): Promise<void> {\n return Promise.resolve(undefined);\n }\n\n private async getResult(req: express.Request): Promise<AwsAlbResult> {\n const jwt = req.header(ALB_JWT_HEADER);\n const accessToken = req.header(ALB_ACCESSTOKEN_HEADER);\n\n if (jwt === undefined) {\n throw new AuthenticationError(\n `Missing ALB OIDC header: ${ALB_JWT_HEADER}`,\n );\n }\n\n if (accessToken === undefined) {\n throw new AuthenticationError(\n `Missing ALB OIDC header: ${ALB_ACCESSTOKEN_HEADER}`,\n );\n }\n\n try {\n const headers = getJWTHeaders(jwt);\n const key = await this.getKey(headers.kid);\n const claims = JWT.verify(jwt, key) as AwsAlbClaims;\n\n if (this.issuer && claims.iss !== this.issuer) {\n throw new AuthenticationError('Issuer mismatch on JWT token');\n }\n\n const fullProfile: PassportProfile = {\n provider: 'unknown',\n id: claims.sub,\n displayName: claims.name,\n username: claims.email.split('@')[0].toLowerCase(),\n name: {\n familyName: claims.family_name,\n givenName: claims.given_name,\n },\n emails: [{ value: claims.email.toLowerCase() }],\n photos: [{ value: claims.picture }],\n };\n\n return {\n fullProfile,\n expiresInSeconds: claims.exp,\n accessToken,\n };\n } catch (e) {\n throw new Error(`Exception occurred during JWT processing: ${e}`);\n }\n }\n\n private async handleResult(result: AwsAlbResult): Promise<AwsAlbResponse> {\n const { profile } = await this.authHandler(result);\n const backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n\n return {\n providerInfo: {\n accessToken: result.accessToken,\n expiresInSeconds: result.expiresInSeconds,\n },\n backstageIdentity,\n profile,\n };\n }\n\n async getKey(keyId: string): Promise<KeyObject> {\n const optionalCacheKey = this.keyCache.get<KeyObject>(keyId);\n if (optionalCacheKey) {\n return crypto.createPublicKey(optionalCacheKey);\n }\n const keyText: string = await fetch(\n `https://public-keys.auth.elb.${this.region}.amazonaws.com/${keyId}`,\n ).then(response => response.text());\n const keyValue = crypto.createPublicKey(keyText);\n this.keyCache.set(keyId, keyValue.export({ format: 'pem', type: 'spki' }));\n return keyValue;\n }\n}\n\nexport type AwsAlbProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<AwsAlbResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n signIn: {\n /**\n * Maps an auth result to a Backstage identity for the user.\n */\n resolver: SignInResolver<AwsAlbResult>;\n };\n};\n\nexport const createAwsAlbProvider = (\n options?: AwsAlbProviderOptions,\n): AuthProviderFactory => {\n return ({ config, tokenIssuer, catalogApi, logger }) => {\n const region = config.getString('region');\n const issuer = config.getOptionalString('iss');\n\n if (options?.signIn.resolver === undefined) {\n throw new Error(\n 'SignInResolver is required to use this authentication provider',\n );\n }\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<AwsAlbResult> = options?.authHandler\n ? options.authHandler\n : async ({ fullProfile }) => ({\n profile: makeProfileInfo(fullProfile),\n });\n\n const signInResolver = options?.signIn.resolver;\n\n return new AwsAlbAuthProvider({\n region,\n issuer,\n signInResolver,\n authHandler,\n tokenIssuer,\n catalogIdentityClient,\n logger,\n });\n };\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport {\n Issuer,\n Client,\n Strategy as OidcStrategy,\n TokenSet,\n UserinfoResponse,\n} from 'openid-client';\nimport {\n OAuthAdapter,\n OAuthProviderOptions,\n OAuthHandlers,\n OAuthResponse,\n OAuthEnvironmentHandler,\n OAuthStartRequest,\n encodeState,\n OAuthRefreshRequest,\n} from '../../lib/oauth';\nimport {\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport { RedirectInfo, AuthProviderFactory } from '../types';\n\ntype PrivateInfo = {\n refreshToken?: string;\n};\n\ntype OidcImpl = {\n strategy: OidcStrategy<UserinfoResponse, Client>;\n client: Client;\n};\n\ntype AuthResult = {\n tokenset: TokenSet;\n userinfo: UserinfoResponse;\n};\n\nexport type Options = OAuthProviderOptions & {\n metadataUrl: string;\n scope?: string;\n prompt?: string;\n tokenSignedResponseAlg?: string;\n};\n\nexport class OidcAuthProvider implements OAuthHandlers {\n private readonly implementation: Promise<OidcImpl>;\n private readonly scope?: string;\n private readonly prompt?: string;\n\n constructor(options: Options) {\n this.implementation = this.setupStrategy(options);\n this.scope = options.scope;\n this.prompt = options.prompt;\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n const { strategy } = await this.implementation;\n const options: Record<string, string> = {\n accessType: 'offline',\n scope: req.scope || this.scope || 'openid profile email',\n state: encodeState(req.state),\n };\n const prompt = this.prompt || 'none';\n if (prompt !== 'auto') {\n options.prompt = prompt;\n }\n return await executeRedirectStrategy(req, strategy, options);\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken?: string }> {\n const { strategy } = await this.implementation;\n const strategyResponse = await executeFrameHandlerStrategy<\n AuthResult,\n PrivateInfo\n >(req, strategy);\n const {\n result: { userinfo, tokenset },\n privateInfo,\n } = strategyResponse;\n const identityResponse = await this.populateIdentity({\n profile: {\n displayName: userinfo.name,\n email: userinfo.email,\n picture: userinfo.picture,\n },\n providerInfo: {\n idToken: tokenset.id_token,\n accessToken: tokenset.access_token || '',\n scope: tokenset.scope || '',\n expiresInSeconds: tokenset.expires_in,\n },\n });\n return {\n response: identityResponse,\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const { client } = await this.implementation;\n const tokenset = await client.refresh(req.refreshToken);\n if (!tokenset.access_token) {\n throw new Error('Refresh failed');\n }\n const profile = await client.userinfo(tokenset.access_token);\n\n return this.populateIdentity({\n providerInfo: {\n accessToken: tokenset.access_token,\n refreshToken: tokenset.refresh_token,\n expiresInSeconds: tokenset.expires_in,\n idToken: tokenset.id_token,\n scope: tokenset.scope || '',\n },\n profile,\n });\n }\n\n private async setupStrategy(options: Options): Promise<OidcImpl> {\n const issuer = await Issuer.discover(options.metadataUrl);\n const client = new issuer.Client({\n client_id: options.clientId,\n client_secret: options.clientSecret,\n redirect_uris: [options.callbackUrl],\n response_types: ['code'],\n id_token_signed_response_alg: options.tokenSignedResponseAlg || 'RS256',\n scope: options.scope || '',\n });\n\n const strategy = new OidcStrategy(\n {\n client,\n passReqToCallback: false as true,\n },\n (\n tokenset: TokenSet,\n userinfo: UserinfoResponse,\n done: PassportDoneCallback<AuthResult, PrivateInfo>,\n ) => {\n if (typeof done !== 'function') {\n throw new Error(\n 'OIDC IdP must provide a userinfo_endpoint in the metadata response',\n );\n }\n done(\n undefined,\n { tokenset, userinfo },\n {\n refreshToken: tokenset.refresh_token,\n },\n );\n },\n );\n strategy.error = console.error;\n return { strategy, client };\n }\n\n // Use this function to grab the user profile info from the token\n // Then populate the profile with it\n private async populateIdentity(\n response: OAuthResponse,\n ): Promise<OAuthResponse> {\n const { profile } = response;\n\n if (!profile.email) {\n throw new Error('Profile does not contain an email');\n }\n const id = profile.email.split('@')[0];\n\n return { ...response, backstageIdentity: { id } };\n }\n}\n\nexport type OidcProviderOptions = {};\n\nexport const createOidcProvider = (\n _options?: OidcProviderOptions,\n): AuthProviderFactory => {\n return ({ providerId, globalConfig, config, tokenIssuer }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n const metadataUrl = envConfig.getString('metadataUrl');\n const tokenSignedResponseAlg = envConfig.getOptionalString(\n 'tokenSignedResponseAlg',\n );\n const scope = envConfig.getOptionalString('scope');\n const prompt = envConfig.getOptionalString('prompt');\n\n const provider = new OidcAuthProvider({\n clientId,\n clientSecret,\n callbackUrl,\n tokenSignedResponseAlg,\n metadataUrl,\n scope,\n prompt,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: false,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport { SamlConfig } from 'passport-saml/lib/passport-saml/types';\nimport {\n Strategy as SamlStrategy,\n Profile as SamlProfile,\n VerifyWithoutRequest,\n} from 'passport-saml';\nimport {\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport { AuthProviderRouteHandlers, AuthProviderFactory } from '../types';\nimport { postMessageResponse } from '../../lib/flow';\nimport { TokenIssuer } from '../../identity/types';\nimport { isError } from '@backstage/errors';\n\ntype SamlInfo = {\n fullProfile: any;\n};\n\ntype Options = SamlConfig & {\n tokenIssuer: TokenIssuer;\n appUrl: string;\n};\n\nexport class SamlAuthProvider implements AuthProviderRouteHandlers {\n private readonly strategy: SamlStrategy;\n private readonly tokenIssuer: TokenIssuer;\n private readonly appUrl: string;\n\n constructor(options: Options) {\n this.appUrl = options.appUrl;\n this.tokenIssuer = options.tokenIssuer;\n this.strategy = new SamlStrategy({ ...options }, ((\n fullProfile: SamlProfile,\n done: PassportDoneCallback<SamlInfo>,\n ) => {\n // TODO: There's plenty more validation and profile handling to do here,\n // this provider is currently only intended to validate the provider pattern\n // for non-oauth auth flows.\n // TODO: This flow doesn't issue an identity token that can be used to validate\n // the identity of the user in other backends, which we need in some form.\n done(undefined, { fullProfile });\n }) as VerifyWithoutRequest);\n }\n\n async start(req: express.Request, res: express.Response): Promise<void> {\n const { url } = await executeRedirectStrategy(req, this.strategy, {});\n res.redirect(url);\n }\n\n async frameHandler(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n try {\n const { result } = await executeFrameHandlerStrategy<SamlInfo>(\n req,\n this.strategy,\n );\n\n const id = result.fullProfile.nameID;\n\n const idToken = await this.tokenIssuer.issueToken({\n claims: { sub: id },\n });\n\n return postMessageResponse(res, this.appUrl, {\n type: 'authorization_response',\n response: {\n profile: {\n email: result.fullProfile.email,\n displayName: result.fullProfile.displayName,\n },\n providerInfo: {},\n backstageIdentity: { id, idToken },\n },\n });\n } catch (error) {\n const { name, message } = isError(error)\n ? error\n : new Error('Encountered invalid error'); // Being a bit safe and not forwarding the bad value\n return postMessageResponse(res, this.appUrl, {\n type: 'authorization_response',\n error: { name, message },\n });\n }\n }\n\n async logout(_req: express.Request, res: express.Response): Promise<void> {\n res.send('noop');\n }\n\n identifyEnv(): string | undefined {\n return undefined;\n }\n}\n\ntype SignatureAlgorithm = 'sha1' | 'sha256' | 'sha512';\n\nexport type SamlProviderOptions = {};\n\nexport const createSamlProvider = (\n _options?: SamlProviderOptions,\n): AuthProviderFactory => {\n return ({ providerId, globalConfig, config, tokenIssuer }) => {\n const opts = {\n callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,\n entryPoint: config.getString('entryPoint'),\n logoutUrl: config.getOptionalString('logoutUrl'),\n issuer: config.getString('issuer'),\n cert: config.getString('cert'),\n privateCert: config.getOptionalString('privateKey'),\n authnContext: config.getOptionalStringArray('authnContext'),\n identifierFormat: config.getOptionalString('identifierFormat'),\n decryptionPvk: config.getOptionalString('decryptionPvk'),\n signatureAlgorithm: config.getOptionalString('signatureAlgorithm') as\n | SignatureAlgorithm\n | undefined,\n digestAlgorithm: config.getOptionalString('digestAlgorithm'),\n acceptedClockSkewMs: config.getOptionalNumber('acceptedClockSkewMs'),\n\n tokenIssuer,\n appUrl: globalConfig.appUrl,\n };\n\n return new SamlAuthProvider(opts);\n };\n};\n","/*\n * Copyright 2020 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 OAuth2Strategy from 'passport-oauth2';\n\nexport interface Auth0StrategyOptionsWithRequest {\n clientID: string;\n clientSecret: string;\n callbackURL: string;\n domain: string;\n passReqToCallback: true;\n}\n\nexport default class Auth0Strategy extends OAuth2Strategy {\n constructor(\n options: Auth0StrategyOptionsWithRequest,\n verify: OAuth2Strategy.VerifyFunctionWithRequest,\n ) {\n const optionsWithURLs = {\n ...options,\n authorizationURL: `https://${options.domain}/authorize`,\n tokenURL: `https://${options.domain}/oauth/token`,\n userInfoURL: `https://${options.domain}/userinfo`,\n apiUrl: `https://${options.domain}/api`,\n };\n super(optionsWithURLs, verify);\n }\n}\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport passport from 'passport';\nimport Auth0Strategy from './strategy';\nimport {\n OAuthAdapter,\n OAuthProviderOptions,\n OAuthHandlers,\n OAuthResponse,\n OAuthEnvironmentHandler,\n OAuthStartRequest,\n encodeState,\n OAuthRefreshRequest,\n OAuthResult,\n} from '../../lib/oauth';\nimport {\n executeFetchUserProfileStrategy,\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport { RedirectInfo, AuthProviderFactory } from '../types';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\nexport type Auth0AuthProviderOptions = OAuthProviderOptions & {\n domain: string;\n};\n\nexport class Auth0AuthProvider implements OAuthHandlers {\n private readonly _strategy: Auth0Strategy;\n\n constructor(options: Auth0AuthProviderOptions) {\n this._strategy = new Auth0Strategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n domain: options.domain,\n passReqToCallback: false as true,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(\n undefined,\n {\n fullProfile,\n accessToken,\n refreshToken,\n params,\n },\n {\n refreshToken,\n },\n );\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n accessType: 'offline',\n prompt: 'consent',\n scope: req.scope,\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n const profile = makeProfileInfo(result.fullProfile, result.params.id_token);\n\n return {\n response: await this.populateIdentity({\n profile,\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n }),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const { accessToken, params } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n const profile = makeProfileInfo(fullProfile, params.id_token);\n\n return this.populateIdentity({\n providerInfo: {\n accessToken,\n idToken: params.id_token,\n expiresInSeconds: params.expires_in,\n scope: params.scope,\n },\n profile,\n });\n }\n\n // Use this function to grab the user profile info from the token\n // Then populate the profile with it\n private async populateIdentity(\n response: OAuthResponse,\n ): Promise<OAuthResponse> {\n const { profile } = response;\n\n if (!profile.email) {\n throw new Error('Profile does not contain an email');\n }\n\n const id = profile.email.split('@')[0];\n\n return { ...response, backstageIdentity: { id } };\n }\n}\n\nexport type Auth0ProviderOptions = {};\n\nexport const createAuth0Provider = (\n _options?: Auth0ProviderOptions,\n): AuthProviderFactory => {\n return ({ providerId, globalConfig, config, tokenIssuer }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const domain = envConfig.getString('domain');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n const provider = new Auth0AuthProvider({\n clientId,\n clientSecret,\n callbackUrl,\n domain,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: true,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport { Strategy as OneLoginStrategy } from 'passport-onelogin-oauth';\nimport express from 'express';\nimport {\n OAuthAdapter,\n OAuthProviderOptions,\n OAuthHandlers,\n OAuthResponse,\n OAuthEnvironmentHandler,\n OAuthStartRequest,\n encodeState,\n OAuthRefreshRequest,\n OAuthResult,\n} from '../../lib/oauth';\nimport passport from 'passport';\nimport {\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n executeFetchUserProfileStrategy,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport { RedirectInfo, AuthProviderFactory } from '../types';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\nexport type Options = OAuthProviderOptions & {\n issuer: string;\n};\n\nexport class OneLoginProvider implements OAuthHandlers {\n private readonly _strategy: any;\n\n constructor(options: Options) {\n this._strategy = new OneLoginStrategy(\n {\n issuer: options.issuer,\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n passReqToCallback: false as true,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(\n undefined,\n {\n accessToken,\n refreshToken,\n params,\n fullProfile,\n },\n {\n refreshToken,\n },\n );\n },\n );\n }\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n accessType: 'offline',\n prompt: 'consent',\n scope: 'openid',\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n const profile = makeProfileInfo(result.fullProfile, result.params.id_token);\n\n return {\n response: await this.populateIdentity({\n profile,\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n }),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const { accessToken, params } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n const profile = makeProfileInfo(fullProfile, params.id_token);\n\n return this.populateIdentity({\n providerInfo: {\n accessToken,\n idToken: params.id_token,\n expiresInSeconds: params.expires_in,\n scope: params.scope,\n },\n profile,\n });\n }\n\n private async populateIdentity(\n response: OAuthResponse,\n ): Promise<OAuthResponse> {\n const { profile } = response;\n\n if (!profile.email) {\n throw new Error('OIDC profile contained no email');\n }\n\n const id = profile.email.split('@')[0];\n\n return { ...response, backstageIdentity: { id } };\n }\n}\n\nexport type OneLoginProviderOptions = {};\n\nexport const createOneLoginProvider = (\n _options?: OneLoginProviderOptions,\n): AuthProviderFactory => {\n return ({ providerId, globalConfig, config, tokenIssuer }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const issuer = envConfig.getString('issuer');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n const provider = new OneLoginProvider({\n clientId,\n clientSecret,\n callbackUrl,\n issuer,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: false,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport { createGithubProvider } from './github';\nimport { createGitlabProvider } from './gitlab';\nimport { createGoogleProvider } from './google';\nimport { createOAuth2Provider } from './oauth2';\nimport { createOidcProvider } from './oidc';\nimport { createOktaProvider } from './okta';\nimport { createSamlProvider } from './saml';\nimport { createAuth0Provider } from './auth0';\nimport { createMicrosoftProvider } from './microsoft';\nimport { createOneLoginProvider } from './onelogin';\nimport { AuthProviderFactory } from './types';\nimport { createAwsAlbProvider } from './aws-alb';\nimport { createBitbucketProvider } from './bitbucket';\nimport { createAtlassianProvider } from './atlassian';\n\nexport const factories: { [providerId: string]: AuthProviderFactory } = {\n google: createGoogleProvider(),\n github: createGithubProvider(),\n gitlab: createGitlabProvider(),\n saml: createSamlProvider(),\n okta: createOktaProvider(),\n auth0: createAuth0Provider(),\n microsoft: createMicrosoftProvider(),\n oauth2: createOAuth2Provider(),\n oidc: createOidcProvider(),\n onelogin: createOneLoginProvider(),\n awsalb: createAwsAlbProvider(),\n bitbucket: createBitbucketProvider(),\n atlassian: createAtlassianProvider(),\n};\n","/*\n * Copyright 2020 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 */\n\nimport Router from 'express-promise-router';\nimport { TokenIssuer } from './types';\n\nexport type Options = {\n baseUrl: string;\n tokenIssuer: TokenIssuer;\n};\n\nexport function createOidcRouter(options: Options) {\n const { baseUrl, tokenIssuer } = options;\n\n const router = Router();\n\n const config = {\n issuer: baseUrl,\n token_endpoint: `${baseUrl}/v1/token`,\n userinfo_endpoint: `${baseUrl}/v1/userinfo`,\n jwks_uri: `${baseUrl}/.well-known/jwks.json`,\n response_types_supported: ['id_token'],\n subject_types_supported: ['public'],\n id_token_signing_alg_values_supported: ['RS256'],\n scopes_supported: ['openid'],\n token_endpoint_auth_methods_supported: [],\n claims_supported: ['sub'],\n grant_types_supported: [],\n };\n\n router.get('/.well-known/openid-configuration', (_req, res) => {\n res.json(config);\n });\n\n router.get('/.well-known/jwks.json', async (_req, res) => {\n const { keys } = await tokenIssuer.listPublicKeys();\n res.json({ keys });\n });\n\n router.get('/v1/token', (_req, res) => {\n res.status(501).send('Not Implemented');\n });\n\n router.get('/v1/userinfo', (_req, res) => {\n res.status(501).send('Not Implemented');\n });\n\n return router;\n}\n","/*\n * Copyright 2020 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 */\n\nimport fetch from 'cross-fetch';\nimport { JWK, JWT, JWKS, JSONWebKey } from 'jose';\nimport { BackstageIdentity } from '../providers';\nimport { PluginEndpointDiscovery } from '@backstage/backend-common';\n\nconst CLOCK_MARGIN_S = 10;\n\n/**\n * A identity client to interact with auth-backend\n * and authenticate backstage identity tokens\n *\n * @experimental This is not a stable API yet\n */\nexport class IdentityClient {\n private readonly discovery: PluginEndpointDiscovery;\n private readonly issuer: string;\n private keyStore: JWKS.KeyStore;\n private keyStoreUpdated: number;\n\n constructor(options: { discovery: PluginEndpointDiscovery; issuer: string }) {\n this.discovery = options.discovery;\n this.issuer = options.issuer;\n this.keyStore = new JWKS.KeyStore();\n this.keyStoreUpdated = 0;\n }\n\n /**\n * Verifies the given backstage identity token\n * Returns a BackstageIdentity (user) matching the token.\n * The method throws an error if verification fails.\n */\n async authenticate(token: string | undefined): Promise<BackstageIdentity> {\n // Extract token from header\n if (!token) {\n throw new Error('No token specified');\n }\n // Get signing key matching token\n const key = await this.getKey(token);\n if (!key) {\n throw new Error('No signing key matching token found');\n }\n // Verify token claims and signature\n // Note: Claims must match those set by TokenFactory when issuing tokens\n // Note: verify throws if verification fails\n const decoded = JWT.IdToken.verify(token, key, {\n algorithms: ['ES256'],\n audience: 'backstage',\n issuer: this.issuer,\n }) as { sub: string };\n // Verified, return the matching user as BackstageIdentity\n // TODO: Settle internal user format/properties\n const user: BackstageIdentity = {\n id: decoded.sub,\n idToken: token,\n };\n return user;\n }\n\n /**\n * Parses the given authorization header and returns\n * the bearer token, or null if no bearer token is given\n */\n static getBearerToken(\n authorizationHeader: string | undefined,\n ): string | undefined {\n if (typeof authorizationHeader !== 'string') {\n return undefined;\n }\n const matches = authorizationHeader.match(/Bearer\\s+(\\S+)/i);\n return matches?.[1];\n }\n\n /**\n * Returns the public signing key matching the given jwt token,\n * or null if no matching key was found\n */\n private async getKey(rawJwtToken: string): Promise<JWK.Key | null> {\n const { header, payload } = JWT.decode(rawJwtToken, {\n complete: true,\n }) as {\n header: { kid: string };\n payload: { iat: number };\n };\n\n // Refresh public keys if needed\n // Add a small margin in case clocks are out of sync\n const keyStoreHasKey = !!this.keyStore.get({ kid: header.kid });\n const issuedAfterLastRefresh =\n payload?.iat && payload.iat > this.keyStoreUpdated - CLOCK_MARGIN_S;\n if (!keyStoreHasKey && issuedAfterLastRefresh) {\n await this.refreshKeyStore();\n }\n\n return this.keyStore.get({ kid: header.kid });\n }\n\n /**\n * Lists public part of keys used to sign Backstage Identity tokens\n */\n async listPublicKeys(): Promise<{\n keys: JSONWebKey[];\n }> {\n const url = `${await this.discovery.getBaseUrl(\n 'auth',\n )}/.well-known/jwks.json`;\n const response = await fetch(url);\n\n if (!response.ok) {\n const payload = await response.text();\n const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`;\n throw new Error(message);\n }\n\n const publicKeys: { keys: JSONWebKey[] } = await response.json();\n\n return publicKeys;\n }\n\n /**\n * Fetches public keys and caches them locally\n */\n private async refreshKeyStore(): Promise<void> {\n const now = Date.now() / 1000;\n const publicKeys = await this.listPublicKeys();\n this.keyStore = JWKS.asKeyStore({\n keys: publicKeys.keys.map(key => key as JSONWebKey),\n });\n this.keyStoreUpdated = now;\n }\n}\n","/*\n * Copyright 2020 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 */\n\nimport { TokenIssuer, TokenParams, KeyStore, AnyJWK } from './types';\nimport { JSONWebKey, JWK, JWS } from 'jose';\nimport { Logger } from 'winston';\nimport { v4 as uuid } from 'uuid';\nimport { DateTime } from 'luxon';\n\nconst MS_IN_S = 1000;\n\ntype Options = {\n logger: Logger;\n /** Value of the issuer claim in issued tokens */\n issuer: string;\n /** Key store used for storing signing keys */\n keyStore: KeyStore;\n /** Expiration time of signing keys in seconds */\n keyDurationSeconds: number;\n};\n\n/**\n * A token issuer that is able to issue tokens in a distributed system\n * backed by a single database. Tokens are issued using lazily generated\n * signing keys, where each running instance of the auth service uses its own\n * signing key.\n *\n * The public parts of the keys are all stored in the shared key storage,\n * and any of the instances of the auth service will return the full list\n * of public keys that are currently in storage.\n *\n * Signing keys are automatically rotated at the same interval as the token\n * duration. Expired keys are kept in storage until there are no valid tokens\n * in circulation that could have been signed by that key.\n */\nexport class TokenFactory implements TokenIssuer {\n private readonly issuer: string;\n private readonly logger: Logger;\n private readonly keyStore: KeyStore;\n private readonly keyDurationSeconds: number;\n\n private keyExpiry?: Date;\n private privateKeyPromise?: Promise<JSONWebKey>;\n\n constructor(options: Options) {\n this.issuer = options.issuer;\n this.logger = options.logger;\n this.keyStore = options.keyStore;\n this.keyDurationSeconds = options.keyDurationSeconds;\n }\n\n async issueToken(params: TokenParams): Promise<string> {\n const key = await this.getKey();\n\n const iss = this.issuer;\n const sub = params.claims.sub;\n const ent = params.claims.ent;\n const aud = 'backstage';\n const iat = Math.floor(Date.now() / MS_IN_S);\n const exp = iat + this.keyDurationSeconds;\n\n this.logger.info(`Issuing token for ${sub}, with entities ${ent ?? []}`);\n\n return JWS.sign({ iss, sub, aud, iat, exp, ent }, key, {\n alg: key.alg,\n kid: key.kid,\n });\n }\n\n // This will be called by other services that want to verify ID tokens.\n // It is important that it returns a list of all public keys that could\n // have been used to sign tokens that have not yet expired.\n async listPublicKeys(): Promise<{ keys: AnyJWK[] }> {\n const { items: keys } = await this.keyStore.listKeys();\n\n const validKeys = [];\n const expiredKeys = [];\n\n for (const key of keys) {\n // Allow for a grace period of another full key duration before we remove the keys from the database\n const expireAt = DateTime.fromJSDate(key.createdAt).plus({\n seconds: 3 * this.keyDurationSeconds,\n });\n if (expireAt < DateTime.local()) {\n expiredKeys.push(key);\n } else {\n validKeys.push(key);\n }\n }\n\n // Lazily prune expired keys. This may cause duplicate removals if we have concurrent callers, but w/e\n if (expiredKeys.length > 0) {\n const kids = expiredKeys.map(({ key }) => key.kid);\n\n this.logger.info(`Removing expired signing keys, '${kids.join(\"', '\")}'`);\n\n // We don't await this, just let it run in the background\n this.keyStore.removeKeys(kids).catch(error => {\n this.logger.error(`Failed to remove expired keys, ${error}`);\n });\n }\n\n // NOTE: we're currently only storing public keys, but if we start storing private keys we'd have to convert here\n return { keys: validKeys.map(({ key }) => key) };\n }\n\n private async getKey(): Promise<JSONWebKey> {\n // Make sure that we only generate one key at a time\n if (this.privateKeyPromise) {\n if (\n this.keyExpiry &&\n DateTime.fromJSDate(this.keyExpiry) > DateTime.local()\n ) {\n return this.privateKeyPromise;\n }\n this.logger.info(`Signing key has expired, generating new key`);\n delete this.privateKeyPromise;\n }\n\n this.keyExpiry = DateTime.utc()\n .plus({\n seconds: this.keyDurationSeconds,\n })\n .toJSDate();\n const promise = (async () => {\n // This generates a new signing key to be used to sign tokens until the next key rotation\n const key = await JWK.generate('EC', 'P-256', {\n use: 'sig',\n kid: uuid(),\n alg: 'ES256',\n });\n\n // We're not allowed to use the key until it has been successfully stored\n // TODO: some token verification implementations aggressively cache the list of keys, and\n // don't attempt to fetch new ones even if they encounter an unknown kid. Therefore we\n // may want to keep using the existing key for some period of time until we switch to\n // the new one. This also needs to be implemented cross-service though, meaning new services\n // that boot up need to be able to grab an existing key to use for signing.\n this.logger.info(`Created new signing key ${key.kid}`);\n await this.keyStore.addKey(key.toJWK(false) as unknown as AnyJWK);\n\n // At this point we are allowed to start using the new key\n return key as JSONWebKey;\n })();\n\n this.privateKeyPromise = promise;\n\n try {\n // If we fail to generate a new key, we need to clear the state so that\n // the next caller will try to generate another key.\n await promise;\n } catch (error) {\n this.logger.error(`Failed to generate new signing key, ${error}`);\n delete this.keyExpiry;\n delete this.privateKeyPromise;\n }\n\n return promise;\n }\n}\n","/*\n * Copyright 2020 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 */\n\nimport { resolvePackagePath } from '@backstage/backend-common';\nimport { Knex } from 'knex';\nimport { DateTime } from 'luxon';\nimport { AnyJWK, KeyStore, StoredKey } from './types';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage/plugin-auth-backend',\n 'migrations',\n);\n\nconst TABLE = 'signing_keys';\n\ntype Row = {\n created_at: Date; // row.created_at is a string after being returned from the database\n kid: string;\n key: string;\n};\n\ntype Options = {\n database: Knex;\n};\n\nconst parseDate = (date: string | Date) => {\n const parsedDate =\n typeof date === 'string'\n ? DateTime.fromSQL(date, { zone: 'UTC' })\n : DateTime.fromJSDate(date);\n\n if (!parsedDate.isValid) {\n throw new Error(\n `Failed to parse date, reason: ${parsedDate.invalidReason}, explanation: ${parsedDate.invalidExplanation}`,\n );\n }\n\n return parsedDate.toJSDate();\n};\n\nexport class DatabaseKeyStore implements KeyStore {\n static async create(options: Options): Promise<DatabaseKeyStore> {\n const { database } = options;\n\n await database.migrate.latest({\n directory: migrationsDir,\n });\n\n return new DatabaseKeyStore(options);\n }\n\n private readonly database: Knex;\n\n private constructor(options: Options) {\n this.database = options.database;\n }\n\n async addKey(key: AnyJWK): Promise<void> {\n await this.database<Row>(TABLE).insert({\n kid: key.kid,\n key: JSON.stringify(key),\n });\n }\n\n async listKeys(): Promise<{ items: StoredKey[] }> {\n const rows = await this.database<Row>(TABLE).select();\n\n return {\n items: rows.map(row => ({\n key: JSON.parse(row.key),\n createdAt: parseDate(row.created_at),\n })),\n };\n }\n\n async removeKeys(kids: string[]): Promise<void> {\n await this.database(TABLE).delete().whereIn('kid', kids);\n }\n}\n","/*\n * Copyright 2020 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 */\n\nimport { KeyStore, AnyJWK, StoredKey } from './types';\nimport { DateTime } from 'luxon';\n\nexport class MemoryKeyStore implements KeyStore {\n private readonly keys = new Map<string, { createdAt: Date; key: string }>();\n\n async addKey(key: AnyJWK): Promise<void> {\n this.keys.set(key.kid, {\n createdAt: DateTime.utc().toJSDate(),\n key: JSON.stringify(key),\n });\n }\n\n async removeKeys(kids: string[]): Promise<void> {\n for (const kid of kids) {\n this.keys.delete(kid);\n }\n }\n\n async listKeys(): Promise<{ items: StoredKey[] }> {\n return {\n items: Array.from(this.keys).map(([, { createdAt, key: keyStr }]) => ({\n createdAt,\n key: JSON.parse(keyStr),\n })),\n };\n }\n}\n","/*\n * Copyright 2021 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 */\n\nimport { Logger } from 'winston';\nimport {\n DocumentData,\n Firestore,\n QuerySnapshot,\n Settings,\n WriteResult,\n} from '@google-cloud/firestore';\n\nimport { AnyJWK, KeyStore, StoredKey } from './types';\n\nexport type FirestoreKeyStoreSettings = Settings & Options;\n\ntype Options = {\n path?: string;\n timeout?: number;\n};\n\nexport const DEFAULT_TIMEOUT_MS = 10000;\nexport const DEFAULT_DOCUMENT_PATH = 'sessions';\n\nexport class FirestoreKeyStore implements KeyStore {\n static async create(\n settings?: FirestoreKeyStoreSettings,\n ): Promise<FirestoreKeyStore> {\n const { path, timeout, ...firestoreSettings } = settings ?? {};\n const database = new Firestore(firestoreSettings);\n\n return new FirestoreKeyStore(\n database,\n path ?? DEFAULT_DOCUMENT_PATH,\n timeout ?? DEFAULT_TIMEOUT_MS,\n );\n }\n\n private constructor(\n private readonly database: Firestore,\n private readonly path: string,\n private readonly timeout: number,\n ) {}\n\n static async verifyConnection(\n keyStore: FirestoreKeyStore,\n logger?: Logger,\n ): Promise<void> {\n try {\n await keyStore.verify();\n } catch (error) {\n if (process.env.NODE_ENV !== 'development') {\n throw new Error(\n `Failed to connect to database: ${(error as Error).message}`,\n );\n }\n logger?.warn(\n `Failed to connect to database: ${(error as Error).message}`,\n );\n }\n }\n\n async addKey(key: AnyJWK): Promise<void> {\n await this.withTimeout<WriteResult>(\n this.database\n .collection(this.path)\n .doc(key.kid)\n .set({\n kid: key.kid,\n key: JSON.stringify(key),\n }),\n );\n }\n\n async listKeys(): Promise<{ items: StoredKey[] }> {\n const keys = await this.withTimeout<QuerySnapshot<DocumentData>>(\n this.database.collection(this.path).get(),\n );\n\n return {\n items: keys.docs.map(key => ({\n key: key.data() as AnyJWK,\n createdAt: key.createTime.toDate(),\n })),\n };\n }\n\n async removeKeys(kids: string[]): Promise<void> {\n // This is probably really slow, but it's done async in the background\n for (const kid of kids) {\n await this.withTimeout<WriteResult>(\n this.database.collection(this.path).doc(kid).delete(),\n );\n }\n\n /**\n * This could be achieved with batching but there's a couple of limitations with that:\n *\n * - A batched write can contain a maximum of 500 operations\n * https://firebase.google.com/docs/firestore/manage-data/transactions#batched-writes\n *\n * - The \"in\" operator can combine a maximum of 10 equality clauses\n * https://firebase.google.com/docs/firestore/query-data/queries#in_not-in_and_array-contains-any\n *\n * Example:\n *\n * const batch = this.database.batch();\n * const docs = await this.database\n * .collection(this.path)\n * .where('kid', 'in', kids)\n * .get();\n * docs.forEach(doc => {\n * batch.delete(doc.ref);\n * });\n * await batch.commit();\n *\n */\n }\n\n /**\n * Helper function to allow us to modify the timeout used when\n * performing Firestore database operations.\n *\n * The reason for this is that it seems that there's no other\n * practical solution to change the default timeout of 10mins\n * that Firestore has.\n *\n */\n private async withTimeout<T>(operation: Promise<T>): Promise<T> {\n const timer = new Promise<never>((_, reject) =>\n setTimeout(() => {\n reject(new Error(`Operation timed out after ${this.timeout}ms`));\n }, this.timeout),\n );\n return Promise.race<T>([operation, timer]);\n }\n\n /**\n * Used to verify that the database is reachable.\n */\n private async verify(): Promise<void> {\n await this.withTimeout(this.database.collection(this.path).limit(1).get());\n }\n}\n","/*\n * Copyright 2021 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 */\n\nimport { Logger } from 'winston';\n\nimport { PluginDatabaseManager } from '@backstage/backend-common';\nimport { Config } from '@backstage/config';\n\nimport { DatabaseKeyStore } from './DatabaseKeyStore';\nimport { MemoryKeyStore } from './MemoryKeyStore';\nimport { FirestoreKeyStore } from './FirestoreKeyStore';\nimport { KeyStore } from './types';\n\ntype Options = {\n logger?: Logger;\n database?: PluginDatabaseManager;\n};\n\nexport class KeyStores {\n /**\n * Looks at the `auth.keyStore` section in the application configuration\n * and returns a KeyStore store. Defaults to `database`\n *\n * @returns a KeyStore store\n */\n static async fromConfig(\n config: Config,\n options?: Options,\n ): Promise<KeyStore> {\n const { logger, database } = options ?? {};\n\n const ks = config.getOptionalConfig('auth.keyStore');\n const provider = ks?.getOptionalString('provider') ?? 'database';\n\n logger?.info(`Configuring \"${provider}\" as KeyStore provider`);\n\n if (provider === 'database') {\n if (!database) {\n throw new Error('This KeyStore provider requires a database');\n }\n\n return await DatabaseKeyStore.create({\n database: await database.getClient(),\n });\n }\n\n if (provider === 'memory') {\n return new MemoryKeyStore();\n }\n\n if (provider === 'firestore') {\n const settings = ks?.getConfig(provider);\n\n const keyStore = await FirestoreKeyStore.create({\n projectId: settings?.getOptionalString('projectId'),\n keyFilename: settings?.getOptionalString('keyFilename'),\n host: settings?.getOptionalString('host'),\n port: settings?.getOptionalNumber('port'),\n ssl: settings?.getOptionalBoolean('ssl'),\n path: settings?.getOptionalString('path'),\n timeout: settings?.getOptionalNumber('timeout'),\n });\n\n await FirestoreKeyStore.verifyConnection(keyStore, logger);\n\n return keyStore;\n }\n\n throw new Error(`Unknown KeyStore provider: ${provider}`);\n }\n}\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport cookieParser from 'cookie-parser';\nimport { Logger } from 'winston';\nimport {\n defaultAuthProviderFactories,\n AuthProviderFactory,\n} from '../providers';\nimport {\n PluginDatabaseManager,\n PluginEndpointDiscovery,\n} from '@backstage/backend-common';\nimport { assertError, NotFoundError } from '@backstage/errors';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { Config } from '@backstage/config';\nimport { createOidcRouter, TokenFactory, KeyStores } from '../identity';\nimport session from 'express-session';\nimport passport from 'passport';\nimport { Minimatch } from 'minimatch';\n\ntype ProviderFactories = { [s: string]: AuthProviderFactory };\n\nexport interface RouterOptions {\n logger: Logger;\n database: PluginDatabaseManager;\n config: Config;\n discovery: PluginEndpointDiscovery;\n providerFactories?: ProviderFactories;\n}\n\nexport async function createRouter({\n logger,\n config,\n discovery,\n database,\n providerFactories,\n}: RouterOptions): Promise<express.Router> {\n const router = Router();\n\n const appUrl = config.getString('app.baseUrl');\n const authUrl = await discovery.getExternalBaseUrl('auth');\n\n const keyStore = await KeyStores.fromConfig(config, { logger, database });\n const keyDurationSeconds = 3600;\n\n const tokenIssuer = new TokenFactory({\n issuer: authUrl,\n keyStore,\n keyDurationSeconds,\n logger: logger.child({ component: 'token-factory' }),\n });\n const catalogApi = new CatalogClient({ discoveryApi: discovery });\n\n const secret = config.getOptionalString('auth.session.secret');\n if (secret) {\n router.use(cookieParser(secret));\n // TODO: Configure the server-side session storage. The default MemoryStore is not designed for production\n router.use(session({ secret, saveUninitialized: false, resave: false }));\n router.use(passport.initialize());\n router.use(passport.session());\n } else {\n router.use(cookieParser());\n }\n router.use(express.urlencoded({ extended: false }));\n router.use(express.json());\n\n const allProviderFactories = {\n ...defaultAuthProviderFactories,\n ...providerFactories,\n };\n const providersConfig = config.getConfig('auth.providers');\n const configuredProviders = providersConfig.keys();\n\n const isOriginAllowed = createOriginFilter(config);\n\n for (const [providerId, providerFactory] of Object.entries(\n allProviderFactories,\n )) {\n if (configuredProviders.includes(providerId)) {\n logger.info(`Configuring provider, ${providerId}`);\n try {\n const provider = providerFactory({\n providerId,\n globalConfig: { baseUrl: authUrl, appUrl, isOriginAllowed },\n config: providersConfig.getConfig(providerId),\n logger,\n tokenIssuer,\n discovery,\n catalogApi,\n });\n\n const r = Router();\n\n r.get('/start', provider.start.bind(provider));\n r.get('/handler/frame', provider.frameHandler.bind(provider));\n r.post('/handler/frame', provider.frameHandler.bind(provider));\n if (provider.logout) {\n r.post('/logout', provider.logout.bind(provider));\n }\n if (provider.refresh) {\n r.get('/refresh', provider.refresh.bind(provider));\n }\n\n router.use(`/${providerId}`, r);\n } catch (e) {\n assertError(e);\n if (process.env.NODE_ENV !== 'development') {\n throw new Error(\n `Failed to initialize ${providerId} auth provider, ${e.message}`,\n );\n }\n\n logger.warn(`Skipping ${providerId} auth provider, ${e.message}`);\n\n router.use(`/${providerId}`, () => {\n // If the user added the provider under auth.providers but the clientId and clientSecret etc. were not found.\n throw new NotFoundError(\n `Auth provider registered for '${providerId}' is misconfigured. This could mean the configs under ` +\n `auth.providers.${providerId} are missing or the environment variables used are not defined. ` +\n `Check the auth backend plugin logs when the backend starts to see more details.`,\n );\n });\n }\n } else {\n router.use(`/${providerId}`, () => {\n throw new NotFoundError(\n `No auth provider registered for '${providerId}'`,\n );\n });\n }\n }\n\n router.use(\n createOidcRouter({\n tokenIssuer,\n baseUrl: authUrl,\n }),\n );\n\n router.use('/:provider/', req => {\n const { provider } = req.params;\n throw new NotFoundError(`Unknown auth provider '${provider}'`);\n });\n\n return router;\n}\n\nexport function createOriginFilter(\n config: Config,\n): (origin: string) => boolean {\n const appUrl = config.getString('app.baseUrl');\n const { origin: appOrigin } = new URL(appUrl);\n\n const allowedOrigins = config.getOptionalStringArray(\n 'auth.experimentalExtraAllowedOrigins',\n );\n\n const allowedOriginPatterns =\n allowedOrigins?.map(\n pattern => new Minimatch(pattern, { nocase: true, noglobstar: true }),\n ) ?? [];\n\n return origin => {\n if (origin === appOrigin) {\n return true;\n }\n return allowedOriginPatterns.some(pattern => pattern.match(origin));\n };\n}\n"],"names":["jwtDecoder","pickBy","InputError","crypto","URL","url","NotAllowedError","isError","ConflictError","NotFoundError","parseEntityRef","stringifyEntityRef","RELATION_MEMBER_OF","GithubStrategy","GitlabStrategy","GoogleStrategy","MicrosoftStrategy","OAuth2Strategy","OktaStrategy","BitbucketStrategy","InternalOAuthError","NodeCache","AuthenticationError","JWT","fetch","Issuer","OidcStrategy","SamlStrategy","OneLoginStrategy","Router","JWKS","JWS","DateTime","JWK","uuid","resolvePackagePath","Firestore","CatalogClient","cookieParser","session","passport","express","defaultAuthProviderFactories","Minimatch"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MA8Ba,kBAAkB,CAC7B,SACA,YACgB;AAjClB;AAkCE,MAAI,QAA4B;AAChC,MAAI,QAAQ,UAAU,QAAQ,OAAO,SAAS,GAAG;AAC/C,UAAM,CAAC,cAAc,QAAQ;AAC7B,YAAQ,WAAW;AAAA;AAGrB,MAAI,UAA8B;AAClC,MAAI,QAAQ,WAAW;AACrB,cAAU,QAAQ;AAAA,aACT,QAAQ,UAAU,QAAQ,OAAO,SAAS,GAAG;AACtD,UAAM,CAAC,cAAc,QAAQ;AAC7B,cAAU,WAAW;AAAA;AAGvB,MAAI,cACF,oBAAQ,gBAAR,YAAuB,QAAQ,aAA/B,YAA2C,QAAQ;AAErD,MAAK,EAAC,SAAS,CAAC,WAAW,CAAC,gBAAgB,SAAS;AACnD,QAAI;AACF,YAAM,UAAkCA,+BAAW;AACnD,UAAI,CAAC,SAAS,QAAQ,OAAO;AAC3B,gBAAQ,QAAQ;AAAA;AAElB,UAAI,CAAC,WAAW,QAAQ,SAAS;AAC/B,kBAAU,QAAQ;AAAA;AAEpB,UAAI,CAAC,eAAe,QAAQ,MAAM;AAChC,sBAAc,QAAQ;AAAA;AAAA,aAEjB,GAAP;AACA,YAAM,IAAI,MAAM,kDAAkD;AAAA;AAAA;AAItE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA;AAAA;MAIS,0BAA0B,OACrC,KACA,kBACA,YAC0B;AAC1B,SAAO,IAAI,QAAQ,aAAW;AAC5B,UAAM,WAAW,OAAO,OAAO;AAC/B,aAAS,WAAW,CAAC,KAAa,WAAoB;AACpD,cAAQ,CAAE,KAAK,QAAQ,0BAAU;AAAA;AAGnC,aAAS,aAAa,KAAK,IAAK;AAAA;AAAA;MAIvB,8BAA8B,OACzC,KACA,qBACG;AACH,SAAO,IAAI,QACT,CAAC,SAAS,WAAW;AACnB,UAAM,WAAW,OAAO,OAAO;AAC/B,aAAS,UAAU,CAAC,QAAa,gBAAqB;AACpD,cAAQ,CAAE,QAAQ;AAAA;AAEpB,aAAS,OAAO,CACd,SAEG;AAvGX;AAwGQ,aAAO,IAAI,MAAM,4BAA4B,WAAK,YAAL,YAAgB;AAAA;AAE/D,aAAS,QAAQ,CAAC,UAA8B;AA1GtD;AA2GQ,UAAI,UAAU,0BAA0B,MAAM;AAE9C,UAAI,YAAM,eAAN,mBAAkB,MAAM;AAC1B,YAAI;AACF,gBAAM,YAAY,KAAK,MAAM,MAAM,WAAW;AAE9C,cAAI,UAAU,SAAS;AACrB,uBAAW,MAAM,UAAU;AAAA;AAAA,iBAEtB,YAAP;AACA,qBAAW,MAAM,MAAM;AAAA;AAAA;AAI3B,aAAO,IAAI,MAAM;AAAA;AAEnB,aAAS,WAAW,MAAM;AACxB,aAAO,IAAI,MAAM;AAAA;AAEnB,aAAS,aAAa,KAAK;AAAA;AAAA;MAiBpB,8BAA8B,OACzC,kBACA,cACA,UACkC;AAClC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,cAAc;AACpB,UAAM,SAAS,YAAY,QAAQ;AACnC,UAAM,SAAS,IAAI,OACjB,YAAY,QAAQ,WACpB,YAAY,QAAQ,eACpB,YAAY,QAAQ,WACpB,YAAY,QAAQ,eACpB,YAAY,eAAe,YAAY,QAAQ,iBAC/C,YAAY,QAAQ;AAGtB,WAAO,oBACL,cACA;AAAA,MACE;AAAA,MACA,YAAY;AAAA,OAEd,CACE,KACA,aACA,iBACA,WACG;AACH,UAAI,KAAK;AACP,eAAO,IAAI,MAAM,kCAAkC,IAAI;AAAA;AAEzD,UAAI,CAAC,aAAa;AAChB,eACE,IAAI,MACF;AAAA;AAKN,cAAQ;AAAA,QACN;AAAA,QACA,cAAc;AAAA,QACd;AAAA;AAAA;AAAA;AAAA;MAWG,kCAAkC,OAC7C,kBACA,gBAC6B;AAC7B,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,cAAc;AACpB,gBAAY,YACV,aACA,CAAC,OAAc,eAAgC;AAC7C,UAAI,OAAO;AACT,eAAO;AAAA,aACF;AACL,gBAAQ;AAAA;AAAA;AAAA;AAAA;;MC7LL,YAAY,CAAC,gBAAoC;AApB9D;AAqBE,QAAM,QAAQ,OAAO,YACnB,IAAI,gBAAgB,OAAO,KAAK,aAAa,OAAO,SAAS;AAE/D,MACE,CAAC,MAAM,SACP,CAAC,MAAM,OACP,aAAM,UAAN,mBAAa,YAAW,KACxB,aAAM,QAAN,mBAAW,YAAW,GACtB;AACA,UAAM,MAAM;AAAA;AAGd,SAAO;AAAA;MAGI,cAAc,CAAC,UAA8B;AACxD,QAAM,cAAc,IAAI,gBACtBC,2BAAe,OAAO,WAAS,UAAU,SACzC;AAEF,SAAO,OAAO,KAAK,aAAa,SAAS,SAAS;AAAA;MAGvC,cAAc,CAAC,KAAsB,eAAuB;AA5CzE;AA6CE,QAAM,cAAc,IAAI,QAAQ,GAAG;AACnC,QAAM,QAAoB,UAAU,gBAAI,MAAM,UAAV,mBAAiB,eAAjB,YAA+B;AACnE,QAAM,aAAa,MAAM;AAEzB,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM;AAAA;AAElB,MAAI,WAAW,WAAW,GAAG;AAC3B,UAAM,IAAI,MAAM;AAAA;AAElB,MAAI,gBAAgB,YAAY;AAC9B,UAAM,IAAI,MAAM;AAAA;AAAA;;8BClCsD;AAAA,EAiBxE,YACmB,UACjB;AADiB;AAAA;AAAA,SAjBZ,UACL,QACA,aACA;AACA,UAAM,OAAO,OAAO;AACpB,UAAM,WAAW,IAAI;AAErB,eAAW,OAAO,MAAM;AACtB,YAAM,YAAY,OAAO,UAAU;AACnC,YAAM,UAAU,YAAY;AAC5B,eAAS,IAAI,KAAK;AAAA;AAGpB,WAAO,IAAI,wBAAwB;AAAA;AAAA,QAO/B,MAAM,KAAsB,KAAsC;AACtE,UAAM,WAAW,KAAK,kBAAkB,KAAK;AAC7C,gDAAgB,MAAM,KAAK;AAAA;AAAA,QAGvB,aACJ,KACA,KACe;AACf,UAAM,WAAW,KAAK,kBAAkB,KAAK;AAC7C,gDAAgB,aAAa,KAAK;AAAA;AAAA,QAG9B,QAAQ,KAAsB,KAAsC;AAxD5E;AAyDI,UAAM,WAAW,KAAK,kBAAkB,KAAK;AAC7C,sDAAgB,YAAV,kCAAoB,KAAK;AAAA;AAAA,QAG3B,OAAO,KAAsB,KAAsC;AA7D3E;AA8DI,UAAM,WAAW,KAAK,kBAAkB,KAAK;AAC7C,sDAAgB,WAAV,kCAAmB,KAAK;AAAA;AAAA,EAGxB,kBAAkB,KAA0C;AAlEtE;AAmEI,UAAM,SAAS,UAAI,MAAM,QAAV,mBAAe;AAC9B,QAAI,QAAQ;AACV,aAAO;AAAA;AAET,UAAM,cAAc,UAAI,MAAM,UAAV,mBAAiB;AACrC,QAAI,CAAC,aAAa;AAChB,aAAO;AAAA;AAET,UAAM,MAAM,UAAU,aAAa;AACnC,WAAO;AAAA;AAAA,EAGD,kBACN,KACA,KACuC;AACvC,UAAM,MAA0B,KAAK,kBAAkB;AAEvD,QAAI,CAAC,KAAK;AACR,YAAM,IAAIC,kBAAW;AAAA;AAGvB,QAAI,CAAC,KAAK,SAAS,IAAI,MAAM;AAC3B,UAAI,OAAO,KAAK,KACd;AAAA;AAAA;AAAA,8EAGsE;AAExE,aAAO;AAAA;AAGT,WAAO,KAAK,SAAS,IAAI;AAAA;AAAA;;MC/EhB,2BAA2B,CAAC,UAAkB;AAGzD,SAAO,mBAAmB,OAAO,QAAQ,MAAM;AAAA;MAGpC,sBAAsB,CACjC,KACA,WACA,aACG;AACH,QAAM,WAAW,KAAK,UAAU;AAChC,QAAM,aAAa,yBAAyB;AAC5C,QAAM,eAAe,yBAAyB;AAmB9C,QAAM,SAAS;AAAA,6CAC4B;AAAA,uCACN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQrC,QAAM,OAAOC,2BAAO,WAAW,UAAU,OAAO,QAAQ,OAAO;AAE/D,MAAI,UAAU,gBAAgB;AAC9B,MAAI,UAAU,mBAAmB;AACjC,MAAI,UAAU,2BAA2B,sBAAsB;AAC/D,MAAI,IAAI,uBAAuB;AAAA;MAGpB,wBAAwB,CAAC,QAAyB;AAC7D,QAAM,iBAAiB,IAAI,OAAO;AAClC,MAAI,CAAC,kBAAkB,mBAAmB,kBAAkB;AAC1D,WAAO;AAAA;AAET,SAAO;AAAA;;MCxCI,mBAAmB,MAAO,KAAK,KAAK,KAAK;MACzC,iBAAiB,MAAM;mBAc2B;AAAA,EAuB7D,YACmB,UACA,SACjB;AAFiB;AACA;AAuKX,0BAAiB,CAAC,KAAuB,UAAkB;AACjE,UAAI,OAAO,GAAG,KAAK,QAAQ,oBAAoB,OAAO;AAAA,QACpD,QAAQ;AAAA,QACR,QAAQ,KAAK,QAAQ;AAAA,QACrB,UAAU;AAAA,QACV,QAAQ,KAAK,QAAQ;AAAA,QACrB,MAAM,GAAG,KAAK,QAAQ;AAAA,QACtB,UAAU;AAAA;AAAA;AAIN,2BAAkB,CAAC,KAAuB,UAAkB;AAClE,UAAI,OAAO,GAAG,KAAK,QAAQ,oBAAoB,OAAO;AAAA,QACpD,QAAQ;AAAA,QACR,QAAQ,KAAK,QAAQ;AAAA,QACrB,UAAU;AAAA,QACV,QAAQ,KAAK,QAAQ;AAAA,QACrB,MAAM,GAAG,KAAK,QAAQ;AAAA,QACtB,UAAU;AAAA;AAAA;AAIN,+BAAsB,CAAC,KAAsB,eAAuB;AAC1E,aAAO,IAAI,QAAQ,GAAG;AAAA;AAGhB,iCAAwB,CAC9B,KACA,iBACG;AACH,UAAI,OAAO,GAAG,KAAK,QAAQ,4BAA4B,cAAc;AAAA,QACnE,QAAQ;AAAA,QACR,QAAQ,KAAK,QAAQ;AAAA,QACrB,UAAU;AAAA,QACV,QAAQ,KAAK,QAAQ;AAAA,QACrB,MAAM,KAAK,QAAQ;AAAA,QACnB,UAAU;AAAA;AAAA;AAIN,oCAA2B,CAAC,QAA0B;AAC5D,UAAI,OAAO,GAAG,KAAK,QAAQ,4BAA4B,IAAI;AAAA,QACzD,QAAQ;AAAA,QACR,QAAQ,KAAK,QAAQ;AAAA,QACrB,UAAU;AAAA,QACV,QAAQ,KAAK,QAAQ;AAAA,QACrB,MAAM,KAAK,QAAQ;AAAA,QACnB,UAAU;AAAA;AAAA;AAAA;AAAA,SA9OP,WACL,QACA,UACA,SAIc;AACd,UAAM,CAAE,QAAQ,aAAc,IAAIC,QAAI,OAAO;AAC7C,UAAM,SAAS,OAAO,QAAQ,WAAW;AACzC,UAAMC,QAAM,IAAID,QAAI,OAAO;AAC3B,UAAM,aAAa,GAAGC,MAAI,YAAY,QAAQ;AAC9C,WAAO,IAAI,aAAa,UAAU;AAAA,SAC7B;AAAA,MACH;AAAA,MACA,cAAcA,MAAI;AAAA,MAClB;AAAA,MACA;AAAA,MACA,iBAAiB,OAAO;AAAA;AAAA;AAAA,QAStB,MAAM,KAAsB,KAAsC;AA9E1E;AAgFI,UAAM,QAAQ,gBAAI,MAAM,UAAV,mBAAiB,eAAjB,YAA+B;AAC7C,UAAM,MAAM,UAAI,MAAM,QAAV,mBAAe;AAC3B,UAAM,SAAS,UAAI,MAAM,WAAV,mBAAkB;AAEjC,QAAI,CAAC,KAAK;AACR,YAAM,IAAIH,kBAAW;AAAA;AAGvB,QAAI,KAAK,QAAQ,eAAe;AAC9B,WAAK,gBAAgB,KAAK;AAAA;AAG5B,UAAM,QAAQC,2BAAO,YAAY,IAAI,SAAS;AAE9C,SAAK,eAAe,KAAK;AAEzB,UAAM,QAAQ,CAAE,OAAO,KAAK;AAC5B,UAAM,aAAa,OAAO,OAAO,KAAK,CAAE,OAAO;AAE/C,UAAM,CAAE,KAAK,UAAW,MAAM,KAAK,SAAS,MAC1C;AAGF,QAAI,aAAa,UAAU;AAC3B,QAAI,UAAU,YAAY;AAC1B,QAAI,UAAU,kBAAkB;AAChC,QAAI;AAAA;AAAA,QAGA,aACJ,KACA,KACe;AAhHnB;AAiHI,QAAI,YAAY,KAAK,QAAQ;AAE7B,QAAI;AACF,YAAM,QAAoB,UAAU,gBAAI,MAAM,UAAV,mBAAiB,eAAjB,YAA+B;AAEnE,UAAI,MAAM,QAAQ;AAChB,YAAI;AACF,sBAAY,IAAIC,QAAI,MAAM,QAAQ;AAAA,gBAClC;AACA,gBAAM,IAAIE,uBAAgB;AAAA;AAE5B,YAAI,CAAC,KAAK,QAAQ,gBAAgB,YAAY;AAC5C,gBAAM,IAAIA,uBAAgB,WAAW;AAAA;AAAA;AAKzC,kBAAY,KAAK,KAAK,QAAQ;AAE9B,YAAM,CAAE,UAAU,gBAAiB,MAAM,KAAK,SAAS,QAAQ;AAE/D,UAAI,KAAK,QAAQ,eAAe;AAC9B,cAAM,gBAAgB,KAAK,oBACzB,KACA,KAAK,QAAQ;AAEf,iBAAS,aAAa,QAAQ;AAAA;AAGhC,UAAI,gBAAgB,CAAC,KAAK,QAAQ,gBAAgB;AAEhD,aAAK,sBAAsB,KAAK;AAAA;AAGlC,YAAM,KAAK,iBAAiB,SAAS;AAGrC,aAAO,oBAAoB,KAAK,WAAW;AAAA,QACzC,MAAM;AAAA,QACN;AAAA;AAAA,aAEK,OAAP;AACA,YAAM,CAAE,MAAM,WAAYC,eAAQ,SAC9B,QACA,IAAI,MAAM;AAEd,aAAO,oBAAoB,KAAK,WAAW;AAAA,QACzC,MAAM;AAAA,QACN,OAAO,CAAE,MAAM;AAAA;AAAA;AAAA;AAAA,QAKf,OAAO,KAAsB,KAAsC;AACvE,QAAI,CAAC,sBAAsB,MAAM;AAC/B,UAAI,OAAO,KAAK,KAAK;AACrB;AAAA;AAIF,SAAK,yBAAyB;AAE9B,QAAI,OAAO,KAAK,KAAK;AAAA;AAAA,QAGjB,QAAQ,KAAsB,KAAsC;AAlL5E;AAmLI,QAAI,CAAC,sBAAsB,MAAM;AAC/B,UAAI,OAAO,KAAK,KAAK;AACrB;AAAA;AAGF,QAAI,CAAC,KAAK,SAAS,WAAW,KAAK,QAAQ,gBAAgB;AACzD,UACG,OAAO,KACP,KACC,6CAA6C,KAAK,QAAQ;AAE9D;AAAA;AAGF,QAAI;AACF,YAAM,eACJ,IAAI,QAAQ,GAAG,KAAK,QAAQ;AAG9B,UAAI,CAAC,cAAc;AACjB,cAAM,IAAI,MAAM;AAAA;AAGlB,YAAM,QAAQ,gBAAI,MAAM,UAAV,mBAAiB,eAAjB,YAA+B;AAE7C,YAAM,aAAa,OAAO,OAAO,KAAK,CAAE,OAAO;AAG/C,YAAM,WAAW,MAAM,KAAK,SAAS,QACnC;AAGF,YAAM,KAAK,iBAAiB,SAAS;AAErC,UACE,SAAS,aAAa,gBACtB,SAAS,aAAa,iBAAiB,cACvC;AACA,aAAK,sBAAsB,KAAK,SAAS,aAAa;AAAA;AAGxD,UAAI,OAAO,KAAK,KAAK;AAAA,aACd,OAAP;AACA,UAAI,OAAO,KAAK,KAAK,OAAO;AAAA;AAAA;AAAA,QAQlB,iBAAiB,UAA8B;AAC3D,QAAI,CAAC,UAAU;AACb;AAAA;AAGF,QAAI,CAAC,SAAS,SAAS;AACrB,eAAS,UAAU,MAAM,KAAK,QAAQ,YAAY,WAAW;AAAA,QAC3D,QAAQ,CAAE,KAAK,SAAS;AAAA;AAAA;AAAA;AAAA;;4BCrMG;AAAA,EAIjC,YAAY,SAA+D;AACzE,SAAK,aAAa,QAAQ;AAC1B,SAAK,cAAc,QAAQ;AAAA;AAAA,QAQvB,SAAS,OAAuC;AACpD,UAAM,SAAiC;AAAA,MACrC,MAAM;AAAA;AAER,eAAW,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,cAAc;AAC5D,aAAO,wBAAwB,SAAS;AAAA;AAI1C,UAAM,QAAQ,MAAM,KAAK,YAAY,WAAW;AAAA,MAC9C,QAAQ,CAAE,KAAK;AAAA;AAEjB,UAAM,CAAE,SAAU,MAAM,KAAK,WAAW,YAAY,CAAE,SAAU,CAAE;AAElE,QAAI,MAAM,WAAW,GAAG;AACtB,UAAI,MAAM,SAAS,GAAG;AACpB,cAAM,IAAIC,qBAAc;AAAA,aACnB;AACL,cAAM,IAAIC,qBAAc;AAAA;AAAA;AAI5B,WAAO,MAAM;AAAA;AAAA,QAUT,yBAAyB;AAAA,IAC7B;AAAA,IACA;AAAA,KACsC;AACtC,UAAM,qBAAqB,WACxB,IAAI,CAAC,QAAgB;AACpB,UAAI;AACF,cAAM,YAAYC,4BAAe,IAAI,kBAAkB,UAAU;AAAA,UAC/D,aAAa;AAAA,UACb,kBAAkB;AAAA;AAEpB,eAAO;AAAA,cACP;AACA,yCAAQ,KAAK,kCAAkC;AAC/C,eAAO;AAAA;AAAA,OAGV,OAAO,CAAC,QAA2B,QAAQ;AAE9C,UAAM,SAAS,mBAAmB,IAAI;AAAQ,MAC5C,MAAM,IAAI;AAAA,MACV,sBAAsB,IAAI;AAAA,MAC1B,iBAAiB,IAAI;AAAA;AAEvB,UAAM,WAAW,MAAM,KAAK,WACzB,YAAY,CAAE,SACd,KAAK,OAAK,EAAE;AAEf,QAAI,WAAW,WAAW,SAAS,QAAQ;AACzC,YAAM,mBAAmB,SAAS,IAAIC;AACtC,YAAM,qBAAqB,mBACxB,IAAIA,iCACJ,OAAO,OAAK,CAAC,iBAAiB,SAAS;AAC1C,uCAAQ,MAAM,+BAA+B,mBAAmB;AAAA;AAGlE,UAAM,WAAW,SAAS,QACxB,OAAE;AA3HR;AA4HQ,2BAAG,cAAH,mBACI,OAAO,OAAK,EAAE,SAASC,iCACxB,IAAI,OAAK,EAAE,YAFd,YAEyB;AAAA;AAG7B,UAAM,gBAAgB;AAAA,MACpB,GAAG,IAAI,IAAI,mBAAmB,OAAO,UAAU,IAAID;AAAA;AAGrD,qCAAQ,MAAM,6BAA6B,cAAc;AACzD,WAAO;AAAA;AAAA;;yBC/GqB,QAA2C;AAvB3E;AAwBE,QAAM,UAAUA,gCAAmB;AAEnC,QAAM,iBACJ,mBAAO,cAAP,mBACI,OACA,OACE,EAAE,SAASC,mCACX,EAAE,OAAO,KAAK,kBAAkB,aAAa,SAEhD,IAAI,OAAKD,gCAAmB,EAAE,aANjC,YAM6C;AAE/C,SAAO;AAAA,IACL,KAAK;AAAA,IACL,KAAK,CAAC,SAAS,GAAG;AAAA;AAAA;;yBCsCmC;AAAA,EASvD,YAAY,SAAoC;AAC9C,SAAK,iBAAiB,QAAQ;AAC9B,SAAK,cAAc,QAAQ;AAC3B,SAAK,eAAe,QAAQ;AAC5B,SAAK,cAAc,QAAQ;AAC3B,SAAK,wBAAwB,QAAQ;AACrC,SAAK,SAAS,QAAQ;AACtB,SAAK,YAAY,IAAIE,yBACnB;AAAA,MACE,UAAU,QAAQ;AAAA,MAClB,cAAc,QAAQ;AAAA,MACtB,aAAa,QAAQ;AAAA,MACrB,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,kBAAkB,QAAQ;AAAA,OAE5B,CACE,aACA,cACA,QACA,aACA,SACG;AACH,WAAK,QAAW,CAAE,aAAa,QAAQ,cAAe,CAAE;AAAA;AAAA;AAAA,QAKxD,MAAM,KAA+C;AACzD,WAAO,MAAM,wBAAwB,KAAK,KAAK,WAAW;AAAA,MACxD,OAAO,IAAI;AAAA,MACX,OAAQ,OAAM,KAAK,aAAa,MAAM;AAAA;AAAA;AAAA,QAIpC,QAAQ,KAAsB;AAClC,UAAM,CAAE,QAAQ,eAAgB,MAAM,4BAGpC,KAAK,KAAK;AAEZ,WAAO;AAAA,MACL,UAAU,MAAM,KAAK,aAAa;AAAA,MAClC,cAAc,YAAY;AAAA;AAAA;AAAA,QAIxB,QAAQ,KAAkD;AAC9D,UAAM;AAAA,MACJ;AAAA,MACA,cAAc;AAAA,MACd;AAAA,QACE,MAAM,4BACR,KAAK,WACL,IAAI,cACJ,IAAI;AAEN,UAAM,cAAc,MAAM,gCACxB,KAAK,WACL;AAEF,WAAO,KAAK,aAAa;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA;AAAA;AAAA,QAIJ,aAAa,QAA2B;AACpD,UAAM,CAAE,WAAY,MAAM,KAAK,YAAY;AAE3C,UAAM,eAAe,OAAO,OAAO;AACnC,UAAM,WAA0B;AAAA,MAC9B,cAAc;AAAA,QACZ,aAAa,OAAO;AAAA,QACpB,cAAc,OAAO;AAAA,QACrB,OAAO,OAAO,OAAO;AAAA,QACrB,kBACE,iBAAiB,SAAY,SAAY,OAAO;AAAA;AAAA,MAEpD;AAAA;AAGF,QAAI,KAAK,gBAAgB;AACvB,eAAS,oBAAoB,MAAM,KAAK,eACtC;AAAA,QACE;AAAA,QACA;AAAA,SAEF;AAAA,QACE,aAAa,KAAK;AAAA,QAClB,uBAAuB,KAAK;AAAA,QAC5B,QAAQ,KAAK;AAAA;AAAA;AAKnB,WAAO;AAAA;AAAA;MAIE,8BACX,OAAO,MAAM,QAAQ;AACnB,QAAM,CAAE,eAAgB,KAAK;AAE7B,QAAM,SAAS,YAAY,YAAY,YAAY;AAEnD,QAAM,QAAQ,MAAM,IAAI,YAAY,WAAW;AAAA,IAC7C,QAAQ,CAAE,KAAK,QAAQ,KAAK,CAAC,gBAAgB;AAAA;AAG/C,SAAO,CAAE,IAAI,QAAQ;AAAA;MAuCZ,uBAAuB,CAClC,YACwB;AACxB,SAAO,CAAC;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,QAEA,wBAAwB,UAAU,QAAQ,eAAa;AAtP3D;AAuPM,UAAM,WAAW,UAAU,UAAU;AACrC,UAAM,eAAe,UAAU,UAAU;AACzC,UAAM,wBAAwB,UAAU,kBACtC;AAEF,UAAM,oBAAoB,UAAU,kBAAkB;AACtD,UAAM,mBAAmB,wBACrB,GAAG,gDACH;AACJ,UAAM,WAAW,wBACb,GAAG,mDACH;AACJ,UAAM,iBAAiB,wBACnB,GAAG,sCACH;AACJ,UAAM,cACJ,qBACA,GAAG,aAAa,WAAW;AAE7B,UAAM,wBAAwB,IAAI,sBAAsB;AAAA,MACtD;AAAA,MACA;AAAA;AAGF,UAAM,cAA8C,oCAAS,eACzD,QAAQ,cACR,OAAO,CAAE;AAAmB,MAC1B,SAAS,gBAAgB;AAAA;AAG/B,UAAM,mBACJ,+CAAS,WAAT,mBAAiB,aAAjB,YAA6B;AAE/B,UAAM,iBAAoD,UACxD,iBAAiB,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA;AAGJ,UAAM,eACJ,yCAAS,iBAAT,YACC,OAAO,QAA8D;AACpE,aAAO,CAAE,cAAc,YAAY,IAAI;AAAA;AAG3C,UAAM,WAAW,IAAI,mBAAmB;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAGF,WAAO,aAAa,WAAW,cAAc,UAAU;AAAA,MACrD,eAAe;AAAA,MACf;AAAA,MACA;AAAA;AAAA;AAAA;;MC1PK,8BAA2D,OACtE,MACA,QACG;AACH,QAAM,CAAE,SAAS,UAAW;AAE5B,MAAI,KAAK,OAAO,YAAY;AAE5B,MAAI,QAAQ,OAAO;AACjB,SAAK,QAAQ,MAAM,MAAM,KAAK;AAAA;AAGhC,QAAM,QAAQ,MAAM,IAAI,YAAY,WAAW;AAAA,IAC7C,QAAQ,CAAE,KAAK,IAAI,KAAK,CAAC,gBAAgB;AAAA;AAG3C,SAAO,CAAE,IAAI;AAAA;MAGF,2BAAqD,OAAO;AAAA,EACvE;AAAA,EACA;AAAA;AACK,EACL,SAAS,gBAAgB,aAAa,OAAO;AAAA;yBAGU;AAAA,EAQvD,YAAY,SAAoC;AAC9C,SAAK,wBAAwB,QAAQ;AACrC,SAAK,SAAS,QAAQ;AACtB,SAAK,cAAc,QAAQ;AAC3B,SAAK,cAAc,QAAQ;AAC3B,SAAK,iBAAiB,QAAQ;AAE9B,SAAK,YAAY,IAAIC,yBACnB;AAAA,MACE,UAAU,QAAQ;AAAA,MAClB,cAAc,QAAQ;AAAA,MACtB,aAAa,QAAQ;AAAA,MACrB,SAAS,QAAQ;AAAA,OAEnB,CACE,aACA,cACA,QACA,aACA,SACG;AACH,WACE,QACA,CAAE,aAAa,QAAQ,cACvB;AAAA,QACE;AAAA;AAAA;AAAA;AAAA,QAOJ,MAAM,KAA+C;AACzD,WAAO,MAAM,wBAAwB,KAAK,KAAK,WAAW;AAAA,MACxD,OAAO,IAAI;AAAA,MACX,OAAO,YAAY,IAAI;AAAA;AAAA;AAAA,QAIrB,QACJ,KAC4D;AAC5D,UAAM,CAAE,QAAQ,eAAgB,MAAM,4BAGpC,KAAK,KAAK;AAEZ,WAAO;AAAA,MACL,UAAU,MAAM,KAAK,aAAa;AAAA,MAClC,cAAc,YAAY;AAAA;AAAA;AAAA,QAIxB,QAAQ,KAAkD;AAC9D,UAAM;AAAA,MACJ;AAAA,MACA,cAAc;AAAA,MACd;AAAA,QACE,MAAM,4BACR,KAAK,WACL,IAAI,cACJ,IAAI;AAGN,UAAM,cAAc,MAAM,gCACxB,KAAK,WACL;AAGF,WAAO,KAAK,aAAa;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA;AAAA;AAAA,QAIJ,aAAa,QAA6C;AACtE,UAAM,CAAE,WAAY,MAAM,KAAK,YAAY;AAE3C,UAAM,WAA0B;AAAA,MAC9B,cAAc;AAAA,QACZ,SAAS,OAAO,OAAO;AAAA,QACvB,aAAa,OAAO;AAAA,QACpB,cAAc,OAAO;AAAA,QACrB,OAAO,OAAO,OAAO;AAAA,QACrB,kBAAkB,OAAO,OAAO;AAAA;AAAA,MAElC;AAAA;AAGF,QAAI,KAAK,gBAAgB;AACvB,eAAS,oBAAoB,MAAM,KAAK,eACtC;AAAA,QACE;AAAA,QACA;AAAA,SAEF;AAAA,QACE,aAAa,KAAK;AAAA,QAClB,uBAAuB,KAAK;AAAA,QAC5B,QAAQ,KAAK;AAAA;AAAA;AAKnB,WAAO;AAAA;AAAA;MAyBE,uBAAuB,CAClC,YACwB;AACxB,SAAO,CAAC;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,QAEA,wBAAwB,UAAU,QAAQ,eAAa;AA5O3D;AA6OM,UAAM,WAAW,UAAU,UAAU;AACrC,UAAM,eAAe,UAAU,UAAU;AACzC,UAAM,WAAW,UAAU,kBAAkB;AAC7C,UAAM,UAAU,YAAY;AAC5B,UAAM,cAAc,GAAG,aAAa,WAAW;AAE/C,UAAM,wBAAwB,IAAI,sBAAsB;AAAA,MACtD;AAAA,MACA;AAAA;AAGF,UAAM,cACJ,yCAAS,gBAAT,YAAwB;AAE1B,UAAM,mBACJ,+CAAS,WAAT,mBAAiB,aAAjB,YAA6B;AAE/B,UAAM,iBAA8C,UAClD,iBAAiB,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA;AAGJ,UAAM,WAAW,IAAI,mBAAmB;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAGF,WAAO,aAAa,WAAW,cAAc,UAAU;AAAA,MACrD,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA;AAAA;AAAA;;yBCxNiD;AAAA,EAQvD,YAAY,SAAkB;AAC5B,SAAK,iBAAiB,QAAQ;AAC9B,SAAK,cAAc,QAAQ;AAC3B,SAAK,cAAc,QAAQ;AAC3B,SAAK,wBAAwB,QAAQ;AACrC,SAAK,SAAS,QAAQ;AACtB,SAAK,YAAY,IAAIC,+BACnB;AAAA,MACE,UAAU,QAAQ;AAAA,MAClB,cAAc,QAAQ;AAAA,MACtB,aAAa,QAAQ;AAAA,MAGrB,mBAAmB;AAAA,OAErB,CACE,aACA,cACA,QACA,aACA,SACG;AACH,WACE,QACA;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,SAEF;AAAA,QACE;AAAA;AAAA;AAAA;AAAA,QAOJ,MAAM,KAA+C;AACzD,WAAO,MAAM,wBAAwB,KAAK,KAAK,WAAW;AAAA,MACxD,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,OAAO,IAAI;AAAA,MACX,OAAO,YAAY,IAAI;AAAA;AAAA;AAAA,QAIrB,QACJ,KAC4D;AAC5D,UAAM,CAAE,QAAQ,eAAgB,MAAM,4BAGpC,KAAK,KAAK;AAEZ,WAAO;AAAA,MACL,UAAU,MAAM,KAAK,aAAa;AAAA,MAClC,cAAc,YAAY;AAAA;AAAA;AAAA,QAIxB,QAAQ,KAAkD;AAC9D,UAAM,CAAE,aAAa,UAAW,MAAM,4BACpC,KAAK,WACL,IAAI,cACJ,IAAI;AAEN,UAAM,cAAc,MAAM,gCACxB,KAAK,WACL;AAEF,WAAO,KAAK,aAAa;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc,IAAI;AAAA;AAAA;AAAA,QAIR,aAAa,QAAqB;AAC9C,UAAM,CAAE,WAAY,MAAM,KAAK,YAAY;AAE3C,UAAM,WAA0B;AAAA,MAC9B,cAAc;AAAA,QACZ,SAAS,OAAO,OAAO;AAAA,QACvB,aAAa,OAAO;AAAA,QACpB,OAAO,OAAO,OAAO;AAAA,QACrB,kBAAkB,OAAO,OAAO;AAAA;AAAA,MAElC;AAAA;AAGF,QAAI,KAAK,gBAAgB;AACvB,eAAS,oBAAoB,MAAM,KAAK,eACtC;AAAA,QACE;AAAA,QACA;AAAA,SAEF;AAAA,QACE,aAAa,KAAK;AAAA,QAClB,uBAAuB,KAAK;AAAA,QAC5B,QAAQ,KAAK;AAAA;AAAA;AAKnB,WAAO;AAAA;AAAA;MAIE,4BAAyD,OACpE,MACA,QACG;AACH,QAAM,CAAE,WAAY;AAEpB,MAAI,CAAC,QAAQ,OAAO;AAClB,UAAM,IAAI,MAAM;AAAA;AAGlB,QAAM,SAAS,MAAM,IAAI,sBAAsB,SAAS;AAAA,IACtD,aAAa;AAAA,MACX,oBAAoB,QAAQ;AAAA;AAAA;AAIhC,QAAM,SAAS,gBAAgB;AAC/B,QAAM,QAAQ,MAAM,IAAI,YAAY,WAAW,CAAE;AAEjD,SAAO,CAAE,IAAI,OAAO,SAAS,MAAM,QAAQ;AAAA;AAG7C,MAAM,8BAA2D,OAC/D,MACA,QACG;AACH,QAAM,CAAE,WAAY;AAEpB,MAAI,CAAC,QAAQ,OAAO;AAClB,UAAM,IAAI,MAAM;AAAA;AAGlB,MAAI;AACJ,MAAI;AACF,UAAM,SAAS,MAAM,IAAI,sBAAsB,SAAS;AAAA,MACtD,aAAa;AAAA,QACX,oBAAoB,QAAQ;AAAA;AAAA;AAGhC,aAAS,OAAO,SAAS;AAAA,WAClB,OAAP;AACA,QAAI,OAAO,KACT,2BAA2B;AAE7B,aAAS,QAAQ,MAAM,MAAM,KAAK;AAAA;AAGpC,QAAM,QAAQ,MAAM,IAAI,YAAY,WAAW;AAAA,IAC7C,QAAQ,CAAE,KAAK,QAAQ,KAAK,CAAC,gBAAgB;AAAA;AAG/C,SAAO,CAAE,IAAI,QAAQ;AAAA;MAqBV,uBAAuB,CAClC,YACwB;AACxB,SAAO,CAAC;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,QAEA,wBAAwB,UAAU,QAAQ,eAAa;AArQ3D;AAsQM,UAAM,WAAW,UAAU,UAAU;AACrC,UAAM,eAAe,UAAU,UAAU;AACzC,UAAM,cAAc,GAAG,aAAa,WAAW;AAE/C,UAAM,wBAAwB,IAAI,sBAAsB;AAAA,MACtD;AAAA,MACA;AAAA;AAGF,UAAM,cAAwC,oCAAS,eACnD,QAAQ,cACR,OAAO,CAAE,aAAa;AAAc,MAClC,SAAS,gBAAgB,aAAa,OAAO;AAAA;AAGnD,UAAM,mBACJ,+CAAS,WAAT,mBAAiB,aAAjB,YAA6B;AAE/B,UAAM,iBAA8C,UAClD,iBAAiB,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA;AAGJ,UAAM,WAAW,IAAI,mBAAmB;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAGF,WAAO,aAAa,WAAW,cAAc,UAAU;AAAA,MACrD,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA;AAAA;AAAA;;4BC9OoD;AAAA,EAQ1D,YAAY,SAAkB;AAC5B,SAAK,iBAAiB,QAAQ;AAC9B,SAAK,cAAc,QAAQ;AAC3B,SAAK,cAAc,QAAQ;AAC3B,SAAK,SAAS,QAAQ;AACtB,SAAK,wBAAwB,QAAQ;AAErC,SAAK,YAAY,IAAIC,2BACnB;AAAA,MACE,UAAU,QAAQ;AAAA,MAClB,cAAc,QAAQ;AAAA,MACtB,aAAa,QAAQ;AAAA,MACrB,kBAAkB,QAAQ;AAAA,MAC1B,UAAU,QAAQ;AAAA,MAClB,mBAAmB;AAAA,OAErB,CACE,aACA,cACA,QACA,aACA,SACG;AACH,WAAK,QAAW,CAAE,aAAa,aAAa,SAAU,CAAE;AAAA;AAAA;AAAA,QAKxD,MAAM,KAA+C;AACzD,WAAO,MAAM,wBAAwB,KAAK,KAAK,WAAW;AAAA,MACxD,OAAO,IAAI;AAAA,MACX,OAAO,YAAY,IAAI;AAAA;AAAA;AAAA,QAIrB,QACJ,KAC4D;AAC5D,UAAM,CAAE,QAAQ,eAAgB,MAAM,4BAGpC,KAAK,KAAK;AAEZ,WAAO;AAAA,MACL,UAAU,MAAM,KAAK,aAAa;AAAA,MAClC,cAAc,YAAY;AAAA;AAAA;AAAA,QAIxB,QAAQ,KAAkD;AAC9D,UAAM,CAAE,aAAa,UAAW,MAAM,4BACpC,KAAK,WACL,IAAI,cACJ,IAAI;AAGN,UAAM,cAAc,MAAM,gCACxB,KAAK,WACL;AAGF,WAAO,KAAK,aAAa;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc,IAAI;AAAA;AAAA;AAAA,QAIR,aAAa,QAAqB;AAC9C,UAAM,QAAQ,MAAM,KAAK,aAAa,OAAO;AAC7C,WAAO,YAAY,SAAS,QAAQ,CAAC,CAAE,OAAO,UAAW;AAEzD,UAAM,CAAE,WAAY,MAAM,KAAK,YAAY;AAE3C,UAAM,WAA0B;AAAA,MAC9B,cAAc;AAAA,QACZ,SAAS,OAAO,OAAO;AAAA,QACvB,aAAa,OAAO;AAAA,QACpB,OAAO,OAAO,OAAO;AAAA,QACrB,kBAAkB,OAAO,OAAO;AAAA;AAAA,MAElC;AAAA;AAGF,QAAI,KAAK,gBAAgB;AACvB,eAAS,oBAAoB,MAAM,KAAK,eACtC;AAAA,QACE;AAAA,QACA;AAAA,SAEF;AAAA,QACE,aAAa,KAAK;AAAA,QAClB,uBAAuB,KAAK;AAAA,QAC5B,QAAQ,KAAK;AAAA;AAAA;AAKnB,WAAO;AAAA;AAAA,EAGD,aAAa,aAAkD;AACrE,WAAO,IAAI,QAAQ,aAAW;AAC5B,8BACG,IAAI,2DAA2D;AAAA,QAC9D,UAAU;AAAA,QACV,cAAc;AAAA,QACd,SAAS;AAAA,UACP,eAAe,UAAU;AAAA;AAAA,SAG5B,KAAK,eAAa;AACjB,cAAM,WAAW,0BAA0B,OAAO,KAChD,UAAU,MACV,SAAS;AACX,gBAAQ;AAAA,SAET,MAAM,WAAS;AACd,aAAK,OAAO,KACV,mEAAmE;AAGrE,gBAAQ;AAAA;AAAA;AAAA;AAAA;MAML,+BAA4D,OACvE,MACA,QACG;AACH,QAAM,CAAE,WAAY;AAEpB,MAAI,CAAC,QAAQ,OAAO;AAClB,UAAM,IAAI,MAAM;AAAA;AAGlB,QAAM,SAAS,MAAM,IAAI,sBAAsB,SAAS;AAAA,IACtD,aAAa;AAAA,MACX,uBAAuB,QAAQ;AAAA;AAAA;AAInC,QAAM,SAAS,gBAAgB;AAC/B,QAAM,QAAQ,MAAM,IAAI,YAAY,WAAW,CAAE;AAEjD,SAAO,CAAE,IAAI,OAAO,SAAS,MAAM,QAAQ;AAAA;MAGhC,iCACX,OAAO,MAAM,QAAQ;AACnB,QAAM,CAAE,WAAY;AAEpB,MAAI,CAAC,QAAQ,OAAO;AAClB,UAAM,IAAI,MAAM;AAAA;AAGlB,QAAM,SAAS,QAAQ,MAAM,MAAM,KAAK;AAExC,QAAM,QAAQ,MAAM,IAAI,YAAY,WAAW;AAAA,IAC7C,QAAQ,CAAE,KAAK,QAAQ,KAAK,CAAC,gBAAgB;AAAA;AAG/C,SAAO,CAAE,IAAI,QAAQ;AAAA;MAqBZ,0BAA0B,CACrC,YACwB;AACxB,SAAO,CAAC;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,QAEA,wBAAwB,UAAU,QAAQ,eAAa;AA5Q3D;AA6QM,UAAM,WAAW,UAAU,UAAU;AACrC,UAAM,eAAe,UAAU,UAAU;AACzC,UAAM,WAAW,UAAU,UAAU;AAErC,UAAM,cAAc,GAAG,aAAa,WAAW;AAC/C,UAAM,mBAAmB,qCAAqC;AAC9D,UAAM,WAAW,qCAAqC;AAEtD,UAAM,wBAAwB,IAAI,sBAAsB;AAAA,MACtD;AAAA,MACA;AAAA;AAGF,UAAM,cAAwC,oCAAS,eACnD,QAAQ,cACR,OAAO,CAAE,aAAa;AAAc,MAClC,SAAS,gBAAgB,aAAa,OAAO;AAAA;AAGnD,UAAM,mBACJ,+CAAS,WAAT,mBAAiB,aAAjB,YAA6B;AAE/B,UAAM,iBAA8C,UAClD,iBAAiB,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA;AAGJ,UAAM,WAAW,IAAI,sBAAsB;AAAA,MACzC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAGF,WAAO,aAAa,WAAW,cAAc,UAAU;AAAA,MACrD,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA;AAAA;AAAA;;yBC3PiD;AAAA,EAQvD,YAAY,SAAoC;AAC9C,SAAK,iBAAiB,QAAQ;AAC9B,SAAK,cAAc,QAAQ;AAC3B,SAAK,cAAc,QAAQ;AAC3B,SAAK,wBAAwB,QAAQ;AACrC,SAAK,SAAS,QAAQ;AAEtB,SAAK,YAAY,IAAIC,wBACnB;AAAA,MACE,UAAU,QAAQ;AAAA,MAClB,cAAc,QAAQ;AAAA,MACtB,aAAa,QAAQ;AAAA,MACrB,kBAAkB,QAAQ;AAAA,MAC1B,UAAU,QAAQ;AAAA,MAClB,mBAAmB;AAAA,MACnB,OAAO,QAAQ;AAAA,OAEjB,CACE,aACA,cACA,QACA,aACA,SACG;AACH,WACE,QACA;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,SAEF;AAAA,QACE;AAAA;AAAA;AAAA;AAAA,QAOJ,MAAM,KAA+C;AACzD,WAAO,MAAM,wBAAwB,KAAK,KAAK,WAAW;AAAA,MACxD,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,OAAO,IAAI;AAAA,MACX,OAAO,YAAY,IAAI;AAAA;AAAA;AAAA,QAIrB,QACJ,KAC4D;AAC5D,UAAM,CAAE,QAAQ,eAAgB,MAAM,4BAGpC,KAAK,KAAK;AAEZ,WAAO;AAAA,MACL,UAAU,MAAM,KAAK,aAAa;AAAA,MAClC,cAAc,YAAY;AAAA;AAAA;AAAA,QAIxB,QAAQ,KAAkD;AAC9D,UAAM,uBAAuB,MAAM,4BACjC,KAAK,WACL,IAAI,cACJ,IAAI;AAEN,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,cAAc;AAAA,QACZ;AAEJ,UAAM,cAAc,MAAM,gCACxB,KAAK,WACL;AAGF,WAAO,KAAK,aAAa;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA;AAAA;AAAA,QAIJ,aAAa,QAAqB;AAC9C,UAAM,CAAE,WAAY,MAAM,KAAK,YAAY;AAE3C,UAAM,WAA0B;AAAA,MAC9B,cAAc;AAAA,QACZ,SAAS,OAAO,OAAO;AAAA,QACvB,aAAa,OAAO;AAAA,QACpB,OAAO,OAAO,OAAO;AAAA,QACrB,kBAAkB,OAAO,OAAO;AAAA,QAChC,cAAc,OAAO;AAAA;AAAA,MAEvB;AAAA;AAGF,QAAI,KAAK,gBAAgB;AACvB,eAAS,oBAAoB,MAAM,KAAK,eACtC;AAAA,QACE;AAAA,QACA;AAAA,SAEF;AAAA,QACE,aAAa,KAAK;AAAA,QAClB,uBAAuB,KAAK;AAAA,QAC5B,QAAQ,KAAK;AAAA;AAAA;AAKnB,WAAO;AAAA;AAAA;MAIE,8BAA2D,OACtE,MACA,QACG;AACH,QAAM,CAAE,WAAY;AAEpB,MAAI,CAAC,QAAQ,OAAO;AAClB,UAAM,IAAI,MAAM;AAAA;AAGlB,QAAM,SAAS,QAAQ,MAAM,MAAM,KAAK;AAExC,QAAM,QAAQ,MAAM,IAAI,YAAY,WAAW;AAAA,IAC7C,QAAQ,CAAE,KAAK,QAAQ,KAAK,CAAC,gBAAgB;AAAA;AAG/C,SAAO,CAAE,IAAI,QAAQ;AAAA;MAWV,uBAAuB,CAClC,YACwB;AACxB,SAAO,CAAC;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,QAEA,wBAAwB,UAAU,QAAQ,eAAa;AArO3D;AAsOM,UAAM,WAAW,UAAU,UAAU;AACrC,UAAM,eAAe,UAAU,UAAU;AACzC,UAAM,cAAc,GAAG,aAAa,WAAW;AAC/C,UAAM,mBAAmB,UAAU,UAAU;AAC7C,UAAM,WAAW,UAAU,UAAU;AACrC,UAAM,QAAQ,UAAU,kBAAkB;AAC1C,UAAM,iBACJ,gBAAU,mBAAmB,sBAA7B,YAAkD;AAEpD,UAAM,wBAAwB,IAAI,sBAAsB;AAAA,MACtD;AAAA,MACA;AAAA;AAGF,UAAM,cAAwC,oCAAS,eACnD,QAAQ,cACR,OAAO,CAAE,aAAa;AAAc,MAClC,SAAS,gBAAgB,aAAa,OAAO;AAAA;AAGnD,UAAM,mBACJ,+CAAS,WAAT,mBAAiB,aAAjB,YAA6B;AAE/B,UAAM,iBAA8C,UAClD,iBAAiB,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA;AAGJ,UAAM,WAAW,IAAI,mBAAmB;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAGF,WAAO,aAAa,WAAW,cAAc,UAAU;AAAA,MACrD;AAAA,MACA;AAAA,MACA;AAAA;AAAA;AAAA;;uBCxN+C;AAAA,EAyBrD,YAAY,SAAkC;AATtC,kBAAqB;AAAA,MAC3B,MAAM,MAAuB,IAAS;AACpC,WAAG,MAAM;AAAA;AAAA,MAEX,OAAO,MAAuB,QAAgB,IAAS;AACrD,WAAG,MAAM;AAAA;AAAA;AAKX,SAAK,kBAAkB,QAAQ;AAC/B,SAAK,eAAe,QAAQ;AAC5B,SAAK,eAAe,QAAQ;AAC5B,SAAK,yBAAyB,QAAQ;AACtC,SAAK,UAAU,QAAQ;AAEvB,SAAK,YAAY,IAAIC,2BACnB;AAAA,MACE,UAAU,QAAQ;AAAA,MAClB,cAAc,QAAQ;AAAA,MACtB,aAAa,QAAQ;AAAA,MACrB,UAAU,QAAQ;AAAA,MAClB,mBAAmB;AAAA,MACnB,OAAO,KAAK;AAAA,MACZ,eAAe;AAAA,OAEjB,CACE,aACA,cACA,QACA,aACA,SACG;AACH,WACE,QACA;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,SAEF;AAAA,QACE;AAAA;AAAA;AAAA;AAAA,QAOJ,MAAM,KAA+C;AACzD,WAAO,MAAM,wBAAwB,KAAK,KAAK,WAAW;AAAA,MACxD,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,OAAO,IAAI;AAAA,MACX,OAAO,YAAY,IAAI;AAAA;AAAA;AAAA,QAIrB,QACJ,KAC4D;AAC5D,UAAM,CAAE,QAAQ,eAAgB,MAAM,4BAGpC,KAAK,KAAK;AAEZ,WAAO;AAAA,MACL,UAAU,MAAM,KAAK,aAAa;AAAA,MAClC,cAAc,YAAY;AAAA;AAAA;AAAA,QAIxB,QAAQ,KAAkD;AAC9D,UAAM,CAAE,aAAa,UAAW,MAAM,4BACpC,KAAK,WACL,IAAI,cACJ,IAAI;AAGN,UAAM,cAAc,MAAM,gCACxB,KAAK,WACL;AAGF,WAAO,KAAK,aAAa;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc,IAAI;AAAA;AAAA;AAAA,QAIR,aAAa,QAAqB;AAC9C,UAAM,CAAE,WAAY,MAAM,KAAK,aAAa;AAE5C,UAAM,WAA0B;AAAA,MAC9B,cAAc;AAAA,QACZ,SAAS,OAAO,OAAO;AAAA,QACvB,aAAa,OAAO;AAAA,QACpB,OAAO,OAAO,OAAO;AAAA,QACrB,kBAAkB,OAAO,OAAO;AAAA;AAAA,MAElC;AAAA;AAGF,QAAI,KAAK,iBAAiB;AACxB,eAAS,oBAAoB,MAAM,KAAK,gBACtC;AAAA,QACE;AAAA,QACA;AAAA,SAEF;AAAA,QACE,aAAa,KAAK;AAAA,QAClB,uBAAuB,KAAK;AAAA,QAC5B,QAAQ,KAAK;AAAA;AAAA;AAKnB,WAAO;AAAA;AAAA;MAIE,0BAAuD,OAClE,MACA,QACG;AACH,QAAM,CAAE,WAAY;AAEpB,MAAI,CAAC,QAAQ,OAAO;AAClB,UAAM,IAAI,MAAM;AAAA;AAGlB,QAAM,SAAS,MAAM,IAAI,sBAAsB,SAAS;AAAA,IACtD,aAAa;AAAA,MACX,kBAAkB,QAAQ;AAAA;AAAA;AAI9B,QAAM,SAAS,gBAAgB;AAC/B,QAAM,QAAQ,MAAM,IAAI,YAAY,WAAW,CAAE;AAEjD,SAAO,CAAE,IAAI,OAAO,SAAS,MAAM,QAAQ;AAAA;MAGhC,4BAAyD,OACpE,MACA,QACG;AACH,QAAM,CAAE,WAAY;AAEpB,MAAI,CAAC,QAAQ,OAAO;AAClB,UAAM,IAAI,MAAM;AAAA;AAIlB,QAAM,SAAS,QAAQ,MAAM,MAAM,KAAK;AAExC,QAAM,QAAQ,MAAM,IAAI,YAAY,WAAW;AAAA,IAC7C,QAAQ,CAAE,KAAK,QAAQ,KAAK,CAAC,gBAAgB;AAAA;AAG/C,SAAO,CAAE,IAAI,QAAQ;AAAA;MAqBV,qBAAqB,CAChC,aACwB;AACxB,SAAO,CAAC;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,QAEA,wBAAwB,UAAU,QAAQ,eAAa;AA/Q3D;AAgRM,UAAM,WAAW,UAAU,UAAU;AACrC,UAAM,eAAe,UAAU,UAAU;AACzC,UAAM,WAAW,UAAU,UAAU;AACrC,UAAM,cAAc,GAAG,aAAa,WAAW;AAK/C,QAAI,CAAC,SAAS,WAAW,aAAa;AACpC,YAAM,IAAI,MAAM;AAAA;AAGlB,UAAM,wBAAwB,IAAI,sBAAsB;AAAA,MACtD;AAAA,MACA;AAAA;AAGF,UAAM,cAAwC,sCAAU,eACpD,SAAS,cACT,OAAO,CAAE,aAAa;AAAc,MAClC,SAAS,gBAAgB,aAAa,OAAO;AAAA;AAGnD,UAAM,mBACJ,iDAAU,WAAV,mBAAkB,aAAlB,YAA8B;AAEhC,UAAM,iBAA8C,UAClD,iBAAiB,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA;AAGJ,UAAM,WAAW,IAAI,iBAAiB;AAAA,MACpC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAGF,WAAO,aAAa,WAAW,cAAc,UAAU;AAAA,MACrD,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA;AAAA;AAAA;;4BC3OoD;AAAA,EAQ1D,YAAY,SAAkB;AAC5B,SAAK,iBAAiB,QAAQ;AAC9B,SAAK,cAAc,QAAQ;AAC3B,SAAK,cAAc,QAAQ;AAC3B,SAAK,wBAAwB,QAAQ;AACrC,SAAK,SAAS,QAAQ;AACtB,SAAK,YAAY,IAAIC,iCACnB;AAAA,MACE,UAAU,QAAQ;AAAA,MAClB,cAAc,QAAQ;AAAA,MACtB,aAAa,QAAQ;AAAA,MAGrB,mBAAmB;AAAA,OAErB,CACE,aACA,cACA,QACA,aACA,SACG;AACH,WACE,QACA;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,SAEF;AAAA,QACE;AAAA;AAAA;AAAA;AAAA,QAOJ,MAAM,KAA+C;AACzD,WAAO,MAAM,wBAAwB,KAAK,KAAK,WAAW;AAAA,MACxD,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,OAAO,IAAI;AAAA,MACX,OAAO,YAAY,IAAI;AAAA;AAAA;AAAA,QAIrB,QACJ,KAC4D;AAC5D,UAAM,CAAE,QAAQ,eAAgB,MAAM,4BAGpC,KAAK,KAAK;AAEZ,WAAO;AAAA,MACL,UAAU,MAAM,KAAK,aAAa;AAAA,MAClC,cAAc,YAAY;AAAA;AAAA;AAAA,QAIxB,QAAQ,KAAkD;AAC9D,UAAM,CAAE,aAAa,UAAW,MAAM,4BACpC,KAAK,WACL,IAAI,cACJ,IAAI;AAEN,UAAM,cAAc,MAAM,gCACxB,KAAK,WACL;AAEF,WAAO,KAAK,aAAa;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc,IAAI;AAAA;AAAA;AAAA,QAIR,aAAa,QAA8B;AACvD,WAAO,YAAY,YACjB,OAAO,YAAY,MAAO,MAAO,OAAQ;AAC3C,UAAM,CAAE,WAAY,MAAM,KAAK,YAAY;AAE3C,UAAM,WAA0B;AAAA,MAC9B,cAAc;AAAA,QACZ,SAAS,OAAO,OAAO;AAAA,QACvB,aAAa,OAAO;AAAA,QACpB,OAAO,OAAO,OAAO;AAAA,QACrB,kBAAkB,OAAO,OAAO;AAAA;AAAA,MAElC;AAAA;AAGF,QAAI,KAAK,gBAAgB;AACvB,eAAS,oBAAoB,MAAM,KAAK,eACtC;AAAA,QACE;AAAA,QACA;AAAA,SAEF;AAAA,QACE,aAAa,KAAK;AAAA,QAClB,uBAAuB,KAAK;AAAA,QAC5B,QAAQ,KAAK;AAAA;AAAA;AAKnB,WAAO;AAAA;AAAA;MAIE,kCACX,OAAO,MAAM,QAAQ;AACnB,QAAM,CAAE,UAAW;AAEnB,MAAI,CAAC,OAAO,YAAY,UAAU;AAChC,UAAM,IAAI,MAAM;AAAA;AAGlB,QAAM,SAAS,MAAM,IAAI,sBAAsB,SAAS;AAAA,IACtD,aAAa;AAAA,MACX,0BAA0B,OAAO,YAAY;AAAA;AAAA;AAIjD,QAAM,SAAS,gBAAgB;AAC/B,QAAM,QAAQ,MAAM,IAAI,YAAY,WAAW,CAAE;AAEjD,SAAO,CAAE,IAAI,OAAO,SAAS,MAAM,QAAQ;AAAA;MAGlC,gCACX,OAAO,MAAM,QAAQ;AACnB,QAAM,CAAE,UAAW;AAEnB,MAAI,CAAC,OAAO,YAAY,IAAI;AAC1B,UAAM,IAAI,MAAM;AAAA;AAGlB,QAAM,SAAS,MAAM,IAAI,sBAAsB,SAAS;AAAA,IACtD,aAAa;AAAA,MACX,yBAAyB,OAAO,YAAY;AAAA;AAAA;AAIhD,QAAM,SAAS,gBAAgB;AAC/B,QAAM,QAAQ,MAAM,IAAI,YAAY,WAAW,CAAE;AAEjD,SAAO,CAAE,IAAI,OAAO,SAAS,MAAM,QAAQ;AAAA;MAqBlC,0BAA0B,CACrC,YACwB;AACxB,SAAO,CAAC;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,QAEA,wBAAwB,UAAU,QAAQ,eAAa;AAlR3D;AAmRM,UAAM,WAAW,UAAU,UAAU;AACrC,UAAM,eAAe,UAAU,UAAU;AACzC,UAAM,cAAc,GAAG,aAAa,WAAW;AAE/C,UAAM,wBAAwB,IAAI,sBAAsB;AAAA,MACtD;AAAA,MACA;AAAA;AAGF,UAAM,cACJ,oCAAS,eACL,QAAQ,cACR,OAAO,CAAE,aAAa;AAAc,MAClC,SAAS,gBAAgB,aAAa,OAAO;AAAA;AAGrD,UAAM,WAAW,IAAI,sBAAsB;AAAA,MACzC;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAgB,yCAAS,WAAT,mBAAiB;AAAA,MACjC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAGF,WAAO,aAAa,WAAW,cAAc,UAAU;AAAA,MACrD,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA;AAAA;AAAA;;AC/QR,MAAM,gBAAgB,CAAC,kBAAkB;gCAEMF,mCAAe;AAAA,EAG5D,YACE,SACA,QACA;AACA,QAAI,CAAC,QAAQ,OAAO;AAClB,YAAM,IAAI,UAAU;AAAA;AAGtB,UAAM,SAAS,QAAQ,MAAM,MAAM;AAEnC,UAAM,kBAAkB;AAAA,SACnB;AAAA,MACH,kBAAkB;AAAA,MAClB,UAAU;AAAA,MACV,OAAO,MAAM,KAAK,IAAI,IAAI,CAAC,GAAG,eAAe,GAAG;AAAA;AAGlD,UAAM,iBAAiB;AACvB,SAAK,aAAa;AAClB,SAAK,OAAO;AAEZ,SAAK,QAAQ,6BAA6B;AAAA;AAAA,EAG5C,sBAAsB;AACpB,WAAO;AAAA,MACL,UAAU;AAAA,MACV,QAAQ;AAAA;AAAA;AAAA,EAIZ,YACE,aACA,MACM;AACN,SAAK,QAAQ,IAAI,KAAK,YAAY,aAAa,CAAC,KAAK,SAAS;AAC5D,UAAI,KAAK;AACP,eAAO,KACL,IAAIG,kCACF,gCACA,IAAI;AAAA;AAKV,UAAI,CAAC,MAAM;AACT,eAAO,KACL,IAAI,MAAM;AAAA;AAId,UAAI;AACF,cAAM,OAAO,OAAO,SAAS,WAAW,KAAK,aAAa;AAC1D,cAAM,UAAU,kBAAkB,MAAM;AACxC,eAAO,KAAK,MAAM;AAAA,eACX,GAAP;AACA,eAAO,KAAK,IAAI,MAAM;AAAA;AAAA;AAAA;AAAA,SAKrB,MAAM,MAAuB;AAClC,UAAM,OAAO,KAAK,MAAM;AAExB,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,UAAU;AAAA,MACV,UAAU,KAAK;AAAA,MACf,aAAa,KAAK;AAAA,MAClB,QAAQ,CAAC,CAAE,OAAO,KAAK;AAAA,MACvB,QAAQ,CAAC,CAAE,OAAO,KAAK;AAAA;AAAA;AAAA;;MCpDhB,8BAAwD,OAAO;AAAA,EAC1E;AAAA,EACA;AAAA;AACK,EACL,SAAS,gBAAgB,aAAa,OAAO;AAAA;4BAGa;AAAA,EAQ1D,YAAY,SAAuC;AACjD,SAAK,wBAAwB,QAAQ;AACrC,SAAK,SAAS,QAAQ;AACtB,SAAK,cAAc,QAAQ;AAC3B,SAAK,cAAc,QAAQ;AAC3B,SAAK,iBAAiB,QAAQ;AAE9B,SAAK,YAAY,IAAI,kBACnB;AAAA,MACE,UAAU,QAAQ;AAAA,MAClB,cAAc,QAAQ;AAAA,MACtB,aAAa,QAAQ;AAAA,MACrB,OAAO,QAAQ;AAAA,OAEjB,CACE,aACA,cACA,QACA,aACA,SACG;AACH,WAAK,QAAW;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA;AAAA;AAAA,QAMF,MAAM,KAA+C;AACzD,WAAO,MAAM,wBAAwB,KAAK,KAAK,WAAW;AAAA,MACxD,OAAO,YAAY,IAAI;AAAA;AAAA;AAAA,QAIrB,QACJ,KAC4D;AA/GhE;AAgHI,UAAM,CAAE,UAAW,MAAM,4BACvB,KACA,KAAK;AAGP,WAAO;AAAA,MACL,UAAU,MAAM,KAAK,aAAa;AAAA,MAClC,cAAc,aAAO,iBAAP,YAAuB;AAAA;AAAA;AAAA,QAI3B,aAAa,QAA6C;AACtE,UAAM,CAAE,WAAY,MAAM,KAAK,YAAY;AAE3C,UAAM,WAA0B;AAAA,MAC9B,cAAc;AAAA,QACZ,SAAS,OAAO,OAAO;AAAA,QACvB,aAAa,OAAO;AAAA,QACpB,cAAc,OAAO;AAAA,QACrB,OAAO,OAAO,OAAO;AAAA,QACrB,kBAAkB,OAAO,OAAO;AAAA;AAAA,MAElC;AAAA;AAGF,QAAI,KAAK,gBAAgB;AACvB,eAAS,oBAAoB,MAAM,KAAK,eACtC;AAAA,QACE;AAAA,QACA;AAAA,SAEF;AAAA,QACE,aAAa,KAAK;AAAA,QAClB,uBAAuB,KAAK;AAAA,QAC5B,QAAQ,KAAK;AAAA;AAAA;AAKnB,WAAO;AAAA;AAAA,QAGH,QAAQ,KAAkD;AAC9D,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,cAAc;AAAA,QACZ,MAAM,4BACR,KAAK,WACL,IAAI,cACJ,IAAI;AAGN,UAAM,cAAc,MAAM,gCACxB,KAAK,WACL;AAGF,WAAO,KAAK,aAAa;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA;AAAA;AAAA;MAoBP,0BAA0B,CACrC,YACwB;AACxB,SAAO,CAAC;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,QAEA,wBAAwB,UAAU,QAAQ,eAAa;AA7M3D;AA8MM,UAAM,WAAW,UAAU,UAAU;AACrC,UAAM,eAAe,UAAU,UAAU;AACzC,UAAM,SAAS,UAAU,UAAU;AACnC,UAAM,cAAc,GAAG,aAAa,WAAW;AAE/C,UAAM,wBAAwB,IAAI,sBAAsB;AAAA,MACtD;AAAA,MACA;AAAA;AAGF,UAAM,cACJ,yCAAS,gBAAT,YAAwB;AAE1B,UAAM,WAAW,IAAI,sBAAsB;AAAA,MACzC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAgB,yCAAS,WAAT,mBAAiB;AAAA,MACjC;AAAA,MACA;AAAA,MACA;AAAA;AAGF,WAAO,aAAa,WAAW,cAAc,UAAU;AAAA,MACrD,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA;AAAA;AAAA;;MCvMK,iBAAiB;MACjB,yBAAyB;MAYzB,gBAAgB,CAAC,UAAiC;AAC7D,QAAM,UAAU,MAAM,MAAM,KAAK;AACjC,SAAO,KAAK,MAAM,OAAO,KAAK,SAAS,UAAU,SAAS;AAAA;yBA0CS;AAAA,EAUnE,YAAY,SAAkB;AAC5B,SAAK,SAAS,QAAQ;AACtB,SAAK,SAAS,QAAQ;AACtB,SAAK,cAAc,QAAQ;AAC3B,SAAK,iBAAiB,QAAQ;AAC9B,SAAK,cAAc,QAAQ;AAC3B,SAAK,wBAAwB,QAAQ;AACrC,SAAK,SAAS,QAAQ;AACtB,SAAK,WAAW,IAAIC,8BAAU,CAAE,QAAQ;AAAA;AAAA,EAG1C,eAA8B;AAC5B,WAAO,QAAQ,QAAQ;AAAA;AAAA,QAGnB,QAAQ,KAAsB,KAAsC;AACxE,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,UAAU;AACpC,YAAM,WAAW,MAAM,KAAK,aAAa;AACzC,UAAI,KAAK;AAAA,aACF,GAAP;AACA,WAAK,OAAO,MAAM,mDAAmD;AACrE,UAAI,OAAO;AACX,UAAI;AAAA;AAAA;AAAA,EAIR,QAAuB;AACrB,WAAO,QAAQ,QAAQ;AAAA;AAAA,QAGX,UAAU,KAA6C;AACnE,UAAM,MAAM,IAAI,OAAO;AACvB,UAAM,cAAc,IAAI,OAAO;AAE/B,QAAI,QAAQ,QAAW;AACrB,YAAM,IAAIC,2BACR,4BAA4B;AAAA;AAIhC,QAAI,gBAAgB,QAAW;AAC7B,YAAM,IAAIA,2BACR,4BAA4B;AAAA;AAIhC,QAAI;AACF,YAAM,UAAU,cAAc;AAC9B,YAAM,MAAM,MAAM,KAAK,OAAO,QAAQ;AACtC,YAAM,SAASC,SAAI,OAAO,KAAK;AAE/B,UAAI,KAAK,UAAU,OAAO,QAAQ,KAAK,QAAQ;AAC7C,cAAM,IAAID,2BAAoB;AAAA;AAGhC,YAAM,cAA+B;AAAA,QACnC,UAAU;AAAA,QACV,IAAI,OAAO;AAAA,QACX,aAAa,OAAO;AAAA,QACpB,UAAU,OAAO,MAAM,MAAM,KAAK,GAAG;AAAA,QACrC,MAAM;AAAA,UACJ,YAAY,OAAO;AAAA,UACnB,WAAW,OAAO;AAAA;AAAA,QAEpB,QAAQ,CAAC,CAAE,OAAO,OAAO,MAAM;AAAA,QAC/B,QAAQ,CAAC,CAAE,OAAO,OAAO;AAAA;AAG3B,aAAO;AAAA,QACL;AAAA,QACA,kBAAkB,OAAO;AAAA,QACzB;AAAA;AAAA,aAEK,GAAP;AACA,YAAM,IAAI,MAAM,6CAA6C;AAAA;AAAA;AAAA,QAInD,aAAa,QAA+C;AACxE,UAAM,CAAE,WAAY,MAAM,KAAK,YAAY;AAC3C,UAAM,oBAAoB,MAAM,KAAK,eACnC;AAAA,MACE;AAAA,MACA;AAAA,OAEF;AAAA,MACE,aAAa,KAAK;AAAA,MAClB,uBAAuB,KAAK;AAAA,MAC5B,QAAQ,KAAK;AAAA;AAIjB,WAAO;AAAA,MACL,cAAc;AAAA,QACZ,aAAa,OAAO;AAAA,QACpB,kBAAkB,OAAO;AAAA;AAAA,MAE3B;AAAA,MACA;AAAA;AAAA;AAAA,QAIE,OAAO,OAAmC;AAC9C,UAAM,mBAAmB,KAAK,SAAS,IAAe;AACtD,QAAI,kBAAkB;AACpB,aAAOnB,kBAAO,gBAAgB;AAAA;AAEhC,UAAM,UAAkB,MAAMqB,0BAC5B,gCAAgC,KAAK,wBAAwB,SAC7D,KAAK,cAAY,SAAS;AAC5B,UAAM,WAAWrB,kBAAO,gBAAgB;AACxC,SAAK,SAAS,IAAI,OAAO,SAAS,OAAO,CAAE,QAAQ,OAAO,MAAM;AAChE,WAAO;AAAA;AAAA;MAsBE,uBAAuB,CAClC,YACwB;AACxB,SAAO,CAAC,CAAE,QAAQ,aAAa,YAAY,YAAa;AACtD,UAAM,SAAS,OAAO,UAAU;AAChC,UAAM,SAAS,OAAO,kBAAkB;AAExC,QAAI,oCAAS,OAAO,cAAa,QAAW;AAC1C,YAAM,IAAI,MACR;AAAA;AAIJ,UAAM,wBAAwB,IAAI,sBAAsB;AAAA,MACtD;AAAA,MACA;AAAA;AAGF,UAAM,cAAyC,oCAAS,eACpD,QAAQ,cACR,OAAO,CAAE;AAAmB,MAC1B,SAAS,gBAAgB;AAAA;AAG/B,UAAM,iBAAiB,mCAAS,OAAO;AAEvC,WAAO,IAAI,mBAAmB;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA;AAAA;;uBChNiD;AAAA,EAKrD,YAAY,SAAkB;AAC5B,SAAK,iBAAiB,KAAK,cAAc;AACzC,SAAK,QAAQ,QAAQ;AACrB,SAAK,SAAS,QAAQ;AAAA;AAAA,QAGlB,MAAM,KAA+C;AACzD,UAAM,CAAE,YAAa,MAAM,KAAK;AAChC,UAAM,UAAkC;AAAA,MACtC,YAAY;AAAA,MACZ,OAAO,IAAI,SAAS,KAAK,SAAS;AAAA,MAClC,OAAO,YAAY,IAAI;AAAA;AAEzB,UAAM,SAAS,KAAK,UAAU;AAC9B,QAAI,WAAW,QAAQ;AACrB,cAAQ,SAAS;AAAA;AAEnB,WAAO,MAAM,wBAAwB,KAAK,UAAU;AAAA;AAAA,QAGhD,QACJ,KAC6D;AAC7D,UAAM,CAAE,YAAa,MAAM,KAAK;AAChC,UAAM,mBAAmB,MAAM,4BAG7B,KAAK;AACP,UAAM;AAAA,MACJ,QAAQ,CAAE,UAAU;AAAA,MACpB;AAAA,QACE;AACJ,UAAM,mBAAmB,MAAM,KAAK,iBAAiB;AAAA,MACnD,SAAS;AAAA,QACP,aAAa,SAAS;AAAA,QACtB,OAAO,SAAS;AAAA,QAChB,SAAS,SAAS;AAAA;AAAA,MAEpB,cAAc;AAAA,QACZ,SAAS,SAAS;AAAA,QAClB,aAAa,SAAS,gBAAgB;AAAA,QACtC,OAAO,SAAS,SAAS;AAAA,QACzB,kBAAkB,SAAS;AAAA;AAAA;AAG/B,WAAO;AAAA,MACL,UAAU;AAAA,MACV,cAAc,YAAY;AAAA;AAAA;AAAA,QAIxB,QAAQ,KAAkD;AAC9D,UAAM,CAAE,UAAW,MAAM,KAAK;AAC9B,UAAM,WAAW,MAAM,OAAO,QAAQ,IAAI;AAC1C,QAAI,CAAC,SAAS,cAAc;AAC1B,YAAM,IAAI,MAAM;AAAA;AAElB,UAAM,UAAU,MAAM,OAAO,SAAS,SAAS;AAE/C,WAAO,KAAK,iBAAiB;AAAA,MAC3B,cAAc;AAAA,QACZ,aAAa,SAAS;AAAA,QACtB,cAAc,SAAS;AAAA,QACvB,kBAAkB,SAAS;AAAA,QAC3B,SAAS,SAAS;AAAA,QAClB,OAAO,SAAS,SAAS;AAAA;AAAA,MAE3B;AAAA;AAAA;AAAA,QAIU,cAAc,SAAqC;AAC/D,UAAM,SAAS,MAAMsB,oBAAO,SAAS,QAAQ;AAC7C,UAAM,SAAS,IAAI,OAAO,OAAO;AAAA,MAC/B,WAAW,QAAQ;AAAA,MACnB,eAAe,QAAQ;AAAA,MACvB,eAAe,CAAC,QAAQ;AAAA,MACxB,gBAAgB,CAAC;AAAA,MACjB,8BAA8B,QAAQ,0BAA0B;AAAA,MAChE,OAAO,QAAQ,SAAS;AAAA;AAG1B,UAAM,WAAW,IAAIC,sBACnB;AAAA,MACE;AAAA,MACA,mBAAmB;AAAA,OAErB,CACE,UACA,UACA,SACG;AACH,UAAI,OAAO,SAAS,YAAY;AAC9B,cAAM,IAAI,MACR;AAAA;AAGJ,WACE,QACA,CAAE,UAAU,WACZ;AAAA,QACE,cAAc,SAAS;AAAA;AAAA;AAK/B,aAAS,QAAQ,QAAQ;AACzB,WAAO,CAAE,UAAU;AAAA;AAAA,QAKP,iBACZ,UACwB;AACxB,UAAM,CAAE,WAAY;AAEpB,QAAI,CAAC,QAAQ,OAAO;AAClB,YAAM,IAAI,MAAM;AAAA;AAElB,UAAM,KAAK,QAAQ,MAAM,MAAM,KAAK;AAEpC,WAAO,IAAK,UAAU,mBAAmB,CAAE;AAAA;AAAA;MAMlC,qBAAqB,CAChC,aACwB;AACxB,SAAO,CAAC,CAAE,YAAY,cAAc,QAAQ,iBAC1C,wBAAwB,UAAU,QAAQ,eAAa;AACrD,UAAM,WAAW,UAAU,UAAU;AACrC,UAAM,eAAe,UAAU,UAAU;AACzC,UAAM,cAAc,GAAG,aAAa,WAAW;AAC/C,UAAM,cAAc,UAAU,UAAU;AACxC,UAAM,yBAAyB,UAAU,kBACvC;AAEF,UAAM,QAAQ,UAAU,kBAAkB;AAC1C,UAAM,SAAS,UAAU,kBAAkB;AAE3C,UAAM,WAAW,IAAI,iBAAiB;AAAA,MACpC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAGF,WAAO,aAAa,WAAW,cAAc,UAAU;AAAA,MACrD,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA;AAAA;AAAA;;uBCrL2D;AAAA,EAKjE,YAAY,SAAkB;AAC5B,SAAK,SAAS,QAAQ;AACtB,SAAK,cAAc,QAAQ;AAC3B,SAAK,WAAW,IAAIC,sBAAa,IAAK,UAAY,CAChD,aACA,SACG;AAMH,WAAK,QAAW,CAAE;AAAA;AAAA;AAAA,QAIhB,MAAM,KAAsB,KAAsC;AACtE,UAAM,CAAE,OAAQ,MAAM,wBAAwB,KAAK,KAAK,UAAU;AAClE,QAAI,SAAS;AAAA;AAAA,QAGT,aACJ,KACA,KACe;AACf,QAAI;AACF,YAAM,CAAE,UAAW,MAAM,4BACvB,KACA,KAAK;AAGP,YAAM,KAAK,OAAO,YAAY;AAE9B,YAAM,UAAU,MAAM,KAAK,YAAY,WAAW;AAAA,QAChD,QAAQ,CAAE,KAAK;AAAA;AAGjB,aAAO,oBAAoB,KAAK,KAAK,QAAQ;AAAA,QAC3C,MAAM;AAAA,QACN,UAAU;AAAA,UACR,SAAS;AAAA,YACP,OAAO,OAAO,YAAY;AAAA,YAC1B,aAAa,OAAO,YAAY;AAAA;AAAA,UAElC,cAAc;AAAA,UACd,mBAAmB,CAAE,IAAI;AAAA;AAAA;AAAA,aAGtB,OAAP;AACA,YAAM,CAAE,MAAM,WAAYpB,eAAQ,SAC9B,QACA,IAAI,MAAM;AACd,aAAO,oBAAoB,KAAK,KAAK,QAAQ;AAAA,QAC3C,MAAM;AAAA,QACN,OAAO,CAAE,MAAM;AAAA;AAAA;AAAA;AAAA,QAKf,OAAO,MAAuB,KAAsC;AACxE,QAAI,KAAK;AAAA;AAAA,EAGX,cAAkC;AAChC,WAAO;AAAA;AAAA;MAQE,qBAAqB,CAChC,aACwB;AACxB,SAAO,CAAC,CAAE,YAAY,cAAc,QAAQ,iBAAkB;AAC5D,UAAM,OAAO;AAAA,MACX,aAAa,GAAG,aAAa,WAAW;AAAA,MACxC,YAAY,OAAO,UAAU;AAAA,MAC7B,WAAW,OAAO,kBAAkB;AAAA,MACpC,QAAQ,OAAO,UAAU;AAAA,MACzB,MAAM,OAAO,UAAU;AAAA,MACvB,aAAa,OAAO,kBAAkB;AAAA,MACtC,cAAc,OAAO,uBAAuB;AAAA,MAC5C,kBAAkB,OAAO,kBAAkB;AAAA,MAC3C,eAAe,OAAO,kBAAkB;AAAA,MACxC,oBAAoB,OAAO,kBAAkB;AAAA,MAG7C,iBAAiB,OAAO,kBAAkB;AAAA,MAC1C,qBAAqB,OAAO,kBAAkB;AAAA,MAE9C;AAAA,MACA,QAAQ,aAAa;AAAA;AAGvB,WAAO,IAAI,iBAAiB;AAAA;AAAA;;4BCtHWU,mCAAe;AAAA,EACxD,YACE,SACA,QACA;AACA,UAAM,kBAAkB;AAAA,SACnB;AAAA,MACH,kBAAkB,WAAW,QAAQ;AAAA,MACrC,UAAU,WAAW,QAAQ;AAAA,MAC7B,aAAa,WAAW,QAAQ;AAAA,MAChC,QAAQ,WAAW,QAAQ;AAAA;AAE7B,UAAM,iBAAiB;AAAA;AAAA;;wBCW6B;AAAA,EAGtD,YAAY,SAAmC;AAC7C,SAAK,YAAY,IAAI,cACnB;AAAA,MACE,UAAU,QAAQ;AAAA,MAClB,cAAc,QAAQ;AAAA,MACtB,aAAa,QAAQ;AAAA,MACrB,QAAQ,QAAQ;AAAA,MAChB,mBAAmB;AAAA,OAErB,CACE,aACA,cACA,QACA,aACA,SACG;AACH,WACE,QACA;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,SAEF;AAAA,QACE;AAAA;AAAA;AAAA;AAAA,QAOJ,MAAM,KAA+C;AACzD,WAAO,MAAM,wBAAwB,KAAK,KAAK,WAAW;AAAA,MACxD,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,OAAO,IAAI;AAAA,MACX,OAAO,YAAY,IAAI;AAAA;AAAA;AAAA,QAIrB,QACJ,KAC4D;AAC5D,UAAM,CAAE,QAAQ,eAAgB,MAAM,4BAGpC,KAAK,KAAK;AAEZ,UAAM,UAAU,gBAAgB,OAAO,aAAa,OAAO,OAAO;AAElE,WAAO;AAAA,MACL,UAAU,MAAM,KAAK,iBAAiB;AAAA,QACpC;AAAA,QACA,cAAc;AAAA,UACZ,SAAS,OAAO,OAAO;AAAA,UACvB,aAAa,OAAO;AAAA,UACpB,OAAO,OAAO,OAAO;AAAA,UACrB,kBAAkB,OAAO,OAAO;AAAA;AAAA;AAAA,MAGpC,cAAc,YAAY;AAAA;AAAA;AAAA,QAIxB,QAAQ,KAAkD;AAC9D,UAAM,CAAE,aAAa,UAAW,MAAM,4BACpC,KAAK,WACL,IAAI,cACJ,IAAI;AAGN,UAAM,cAAc,MAAM,gCACxB,KAAK,WACL;AAEF,UAAM,UAAU,gBAAgB,aAAa,OAAO;AAEpD,WAAO,KAAK,iBAAiB;AAAA,MAC3B,cAAc;AAAA,QACZ;AAAA,QACA,SAAS,OAAO;AAAA,QAChB,kBAAkB,OAAO;AAAA,QACzB,OAAO,OAAO;AAAA;AAAA,MAEhB;AAAA;AAAA;AAAA,QAMU,iBACZ,UACwB;AACxB,UAAM,CAAE,WAAY;AAEpB,QAAI,CAAC,QAAQ,OAAO;AAClB,YAAM,IAAI,MAAM;AAAA;AAGlB,UAAM,KAAK,QAAQ,MAAM,MAAM,KAAK;AAEpC,WAAO,IAAK,UAAU,mBAAmB,CAAE;AAAA;AAAA;MAMlC,sBAAsB,CACjC,aACwB;AACxB,SAAO,CAAC,CAAE,YAAY,cAAc,QAAQ,iBAC1C,wBAAwB,UAAU,QAAQ,eAAa;AACrD,UAAM,WAAW,UAAU,UAAU;AACrC,UAAM,eAAe,UAAU,UAAU;AACzC,UAAM,SAAS,UAAU,UAAU;AACnC,UAAM,cAAc,GAAG,aAAa,WAAW;AAE/C,UAAM,WAAW,IAAI,kBAAkB;AAAA,MACrC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAGF,WAAO,aAAa,WAAW,cAAc,UAAU;AAAA,MACrD,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA;AAAA;AAAA;;uBCnI+C;AAAA,EAGrD,YAAY,SAAkB;AAC5B,SAAK,YAAY,IAAIW,+BACnB;AAAA,MACE,QAAQ,QAAQ;AAAA,MAChB,UAAU,QAAQ;AAAA,MAClB,cAAc,QAAQ;AAAA,MACtB,aAAa,QAAQ;AAAA,MACrB,mBAAmB;AAAA,OAErB,CACE,aACA,cACA,QACA,aACA,SACG;AACH,WACE,QACA;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,SAEF;AAAA,QACE;AAAA;AAAA;AAAA;AAAA,QAMJ,MAAM,KAA+C;AACzD,WAAO,MAAM,wBAAwB,KAAK,KAAK,WAAW;AAAA,MACxD,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,OAAO,YAAY,IAAI;AAAA;AAAA;AAAA,QAIrB,QACJ,KAC4D;AAC5D,UAAM,CAAE,QAAQ,eAAgB,MAAM,4BAGpC,KAAK,KAAK;AAEZ,UAAM,UAAU,gBAAgB,OAAO,aAAa,OAAO,OAAO;AAElE,WAAO;AAAA,MACL,UAAU,MAAM,KAAK,iBAAiB;AAAA,QACpC;AAAA,QACA,cAAc;AAAA,UACZ,SAAS,OAAO,OAAO;AAAA,UACvB,aAAa,OAAO;AAAA,UACpB,OAAO,OAAO,OAAO;AAAA,UACrB,kBAAkB,OAAO,OAAO;AAAA;AAAA;AAAA,MAGpC,cAAc,YAAY;AAAA;AAAA;AAAA,QAIxB,QAAQ,KAAkD;AAC9D,UAAM,CAAE,aAAa,UAAW,MAAM,4BACpC,KAAK,WACL,IAAI,cACJ,IAAI;AAGN,UAAM,cAAc,MAAM,gCACxB,KAAK,WACL;AAEF,UAAM,UAAU,gBAAgB,aAAa,OAAO;AAEpD,WAAO,KAAK,iBAAiB;AAAA,MAC3B,cAAc;AAAA,QACZ;AAAA,QACA,SAAS,OAAO;AAAA,QAChB,kBAAkB,OAAO;AAAA,QACzB,OAAO,OAAO;AAAA;AAAA,MAEhB;AAAA;AAAA;AAAA,QAIU,iBACZ,UACwB;AACxB,UAAM,CAAE,WAAY;AAEpB,QAAI,CAAC,QAAQ,OAAO;AAClB,YAAM,IAAI,MAAM;AAAA;AAGlB,UAAM,KAAK,QAAQ,MAAM,MAAM,KAAK;AAEpC,WAAO,IAAK,UAAU,mBAAmB,CAAE;AAAA;AAAA;MAMlC,yBAAyB,CACpC,aACwB;AACxB,SAAO,CAAC,CAAE,YAAY,cAAc,QAAQ,iBAC1C,wBAAwB,UAAU,QAAQ,eAAa;AACrD,UAAM,WAAW,UAAU,UAAU;AACrC,UAAM,eAAe,UAAU,UAAU;AACzC,UAAM,SAAS,UAAU,UAAU;AACnC,UAAM,cAAc,GAAG,aAAa,WAAW;AAE/C,UAAM,WAAW,IAAI,iBAAiB;AAAA,MACpC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAGF,WAAO,aAAa,WAAW,cAAc,UAAU;AAAA,MACrD,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA;AAAA;AAAA;;MCjJK,YAA2D;AAAA,EACtE,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,WAAW;AAAA;;0BCpBoB,SAAkB;AACjD,QAAM,CAAE,SAAS,eAAgB;AAEjC,QAAM,SAASC;AAEf,QAAM,SAAS;AAAA,IACb,QAAQ;AAAA,IACR,gBAAgB,GAAG;AAAA,IACnB,mBAAmB,GAAG;AAAA,IACtB,UAAU,GAAG;AAAA,IACb,0BAA0B,CAAC;AAAA,IAC3B,yBAAyB,CAAC;AAAA,IAC1B,uCAAuC,CAAC;AAAA,IACxC,kBAAkB,CAAC;AAAA,IACnB,uCAAuC;AAAA,IACvC,kBAAkB,CAAC;AAAA,IACnB,uBAAuB;AAAA;AAGzB,SAAO,IAAI,qCAAqC,CAAC,MAAM,QAAQ;AAC7D,QAAI,KAAK;AAAA;AAGX,SAAO,IAAI,0BAA0B,OAAO,MAAM,QAAQ;AACxD,UAAM,CAAE,QAAS,MAAM,YAAY;AACnC,QAAI,KAAK,CAAE;AAAA;AAGb,SAAO,IAAI,aAAa,CAAC,MAAM,QAAQ;AACrC,QAAI,OAAO,KAAK,KAAK;AAAA;AAGvB,SAAO,IAAI,gBAAgB,CAAC,MAAM,QAAQ;AACxC,QAAI,OAAO,KAAK,KAAK;AAAA;AAGvB,SAAO;AAAA;;ACvCT,MAAM,iBAAiB;qBAQK;AAAA,EAM1B,YAAY,SAAiE;AAC3E,SAAK,YAAY,QAAQ;AACzB,SAAK,SAAS,QAAQ;AACtB,SAAK,WAAW,IAAIC,UAAK;AACzB,SAAK,kBAAkB;AAAA;AAAA,QAQnB,aAAa,OAAuD;AAExE,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM;AAAA;AAGlB,UAAM,MAAM,MAAM,KAAK,OAAO;AAC9B,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM;AAAA;AAKlB,UAAM,UAAUP,SAAI,QAAQ,OAAO,OAAO,KAAK;AAAA,MAC7C,YAAY,CAAC;AAAA,MACb,UAAU;AAAA,MACV,QAAQ,KAAK;AAAA;AAIf,UAAM,OAA0B;AAAA,MAC9B,IAAI,QAAQ;AAAA,MACZ,SAAS;AAAA;AAEX,WAAO;AAAA;AAAA,SAOF,eACL,qBACoB;AACpB,QAAI,OAAO,wBAAwB,UAAU;AAC3C,aAAO;AAAA;AAET,UAAM,UAAU,oBAAoB,MAAM;AAC1C,WAAO,mCAAU;AAAA;AAAA,QAOL,OAAO,aAA8C;AACjE,UAAM,CAAE,QAAQ,WAAYA,SAAI,OAAO,aAAa;AAAA,MAClD,UAAU;AAAA;AAQZ,UAAM,iBAAiB,CAAC,CAAC,KAAK,SAAS,IAAI,CAAE,KAAK,OAAO;AACzD,UAAM,yBACJ,oCAAS,QAAO,QAAQ,MAAM,KAAK,kBAAkB;AACvD,QAAI,CAAC,kBAAkB,wBAAwB;AAC7C,YAAM,KAAK;AAAA;AAGb,WAAO,KAAK,SAAS,IAAI,CAAE,KAAK,OAAO;AAAA;AAAA,QAMnC,iBAEH;AACD,UAAM,MAAM,GAAG,MAAM,KAAK,UAAU,WAClC;AAEF,UAAM,WAAW,MAAMC,0BAAM;AAE7B,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,UAAU,MAAM,SAAS;AAC/B,YAAM,UAAU,uBAAuB,SAAS,UAAU,SAAS,eAAe;AAClF,YAAM,IAAI,MAAM;AAAA;AAGlB,UAAM,aAAqC,MAAM,SAAS;AAE1D,WAAO;AAAA;AAAA,QAMK,kBAAiC;AAC7C,UAAM,MAAM,KAAK,QAAQ;AACzB,UAAM,aAAa,MAAM,KAAK;AAC9B,SAAK,WAAWM,UAAK,WAAW;AAAA,MAC9B,MAAM,WAAW,KAAK,IAAI,SAAO;AAAA;AAEnC,SAAK,kBAAkB;AAAA;AAAA;;ACzH3B,MAAM,UAAU;mBA0BiC;AAAA,EAS/C,YAAY,SAAkB;AAC5B,SAAK,SAAS,QAAQ;AACtB,SAAK,SAAS,QAAQ;AACtB,SAAK,WAAW,QAAQ;AACxB,SAAK,qBAAqB,QAAQ;AAAA;AAAA,QAG9B,WAAW,QAAsC;AACrD,UAAM,MAAM,MAAM,KAAK;AAEvB,UAAM,MAAM,KAAK;AACjB,UAAM,MAAM,OAAO,OAAO;AAC1B,UAAM,MAAM,OAAO,OAAO;AAC1B,UAAM,MAAM;AACZ,UAAM,MAAM,KAAK,MAAM,KAAK,QAAQ;AACpC,UAAM,MAAM,MAAM,KAAK;AAEvB,SAAK,OAAO,KAAK,qBAAqB,sBAAsB,oBAAO;AAEnE,WAAOC,SAAI,KAAK,CAAE,KAAK,KAAK,KAAK,KAAK,KAAK,MAAO,KAAK;AAAA,MACrD,KAAK,IAAI;AAAA,MACT,KAAK,IAAI;AAAA;AAAA;AAAA,QAOP,iBAA8C;AAClD,UAAM,CAAE,OAAO,QAAS,MAAM,KAAK,SAAS;AAE5C,UAAM,YAAY;AAClB,UAAM,cAAc;AAEpB,eAAW,OAAO,MAAM;AAEtB,YAAM,WAAWC,eAAS,WAAW,IAAI,WAAW,KAAK;AAAA,QACvD,SAAS,IAAI,KAAK;AAAA;AAEpB,UAAI,WAAWA,eAAS,SAAS;AAC/B,oBAAY,KAAK;AAAA,aACZ;AACL,kBAAU,KAAK;AAAA;AAAA;AAKnB,QAAI,YAAY,SAAS,GAAG;AAC1B,YAAM,OAAO,YAAY,IAAI,CAAC,CAAE,SAAU,IAAI;AAE9C,WAAK,OAAO,KAAK,mCAAmC,KAAK,KAAK;AAG9D,WAAK,SAAS,WAAW,MAAM,MAAM,WAAS;AAC5C,aAAK,OAAO,MAAM,kCAAkC;AAAA;AAAA;AAKxD,WAAO,CAAE,MAAM,UAAU,IAAI,CAAC,CAAE,SAAU;AAAA;AAAA,QAG9B,SAA8B;AAE1C,QAAI,KAAK,mBAAmB;AAC1B,UACE,KAAK,aACLA,eAAS,WAAW,KAAK,aAAaA,eAAS,SAC/C;AACA,eAAO,KAAK;AAAA;AAEd,WAAK,OAAO,KAAK;AACjB,aAAO,KAAK;AAAA;AAGd,SAAK,YAAYA,eAAS,MACvB,KAAK;AAAA,MACJ,SAAS,KAAK;AAAA,OAEf;AACH,UAAM,UAAW,aAAY;AAE3B,YAAM,MAAM,MAAMC,SAAI,SAAS,MAAM,SAAS;AAAA,QAC5C,KAAK;AAAA,QACL,KAAKC;AAAA,QACL,KAAK;AAAA;AASP,WAAK,OAAO,KAAK,2BAA2B,IAAI;AAChD,YAAM,KAAK,SAAS,OAAO,IAAI,MAAM;AAGrC,aAAO;AAAA;AAGT,SAAK,oBAAoB;AAEzB,QAAI;AAGF,YAAM;AAAA,aACC,OAAP;AACA,WAAK,OAAO,MAAM,uCAAuC;AACzD,aAAO,KAAK;AACZ,aAAO,KAAK;AAAA;AAGd,WAAO;AAAA;AAAA;;ACrJX,MAAM,gBAAgBC,iCACpB,kCACA;AAGF,MAAM,QAAQ;AAYd,MAAM,YAAY,CAAC,SAAwB;AACzC,QAAM,aACJ,OAAO,SAAS,WACZH,eAAS,QAAQ,MAAM,CAAE,MAAM,UAC/BA,eAAS,WAAW;AAE1B,MAAI,CAAC,WAAW,SAAS;AACvB,UAAM,IAAI,MACR,iCAAiC,WAAW,+BAA+B,WAAW;AAAA;AAI1F,SAAO,WAAW;AAAA;uBAG8B;AAAA,eACnC,OAAO,SAA6C;AAC/D,UAAM,CAAE,YAAa;AAErB,UAAM,SAAS,QAAQ,OAAO;AAAA,MAC5B,WAAW;AAAA;AAGb,WAAO,IAAI,iBAAiB;AAAA;AAAA,EAKtB,YAAY,SAAkB;AACpC,SAAK,WAAW,QAAQ;AAAA;AAAA,QAGpB,OAAO,KAA4B;AACvC,UAAM,KAAK,SAAc,OAAO,OAAO;AAAA,MACrC,KAAK,IAAI;AAAA,MACT,KAAK,KAAK,UAAU;AAAA;AAAA;AAAA,QAIlB,WAA4C;AAChD,UAAM,OAAO,MAAM,KAAK,SAAc,OAAO;AAE7C,WAAO;AAAA,MACL,OAAO,KAAK,IAAI;AAAQ,QACtB,KAAK,KAAK,MAAM,IAAI;AAAA,QACpB,WAAW,UAAU,IAAI;AAAA;AAAA;AAAA;AAAA,QAKzB,WAAW,MAA+B;AAC9C,UAAM,KAAK,SAAS,OAAO,SAAS,QAAQ,OAAO;AAAA;AAAA;;qBCtEP;AAAA,EAAzC,cAnBP;AAoBmB,gBAAO,IAAI;AAAA;AAAA,QAEtB,OAAO,KAA4B;AACvC,SAAK,KAAK,IAAI,IAAI,KAAK;AAAA,MACrB,WAAWA,eAAS,MAAM;AAAA,MAC1B,KAAK,KAAK,UAAU;AAAA;AAAA;AAAA,QAIlB,WAAW,MAA+B;AAC9C,eAAW,OAAO,MAAM;AACtB,WAAK,KAAK,OAAO;AAAA;AAAA;AAAA,QAIf,WAA4C;AAChD,WAAO;AAAA,MACL,OAAO,MAAM,KAAK,KAAK,MAAM,IAAI,CAAC,GAAG,CAAE,WAAW,KAAK;AAAe,QACpE;AAAA,QACA,KAAK,KAAK,MAAM;AAAA;AAAA;AAAA;AAAA;;MCLX,qBAAqB;MACrB,wBAAwB;wBAEc;AAAA,EAczC,YACW,UACA,MACA,SACjB;AAHiB;AACA;AACA;AAAA;AAAA,eAhBN,OACX,UAC4B;AAC5B,UAAM,CAAE,MAAM,YAAY,qBAAsB,8BAAY;AAC5D,UAAM,WAAW,IAAII,oBAAU;AAE/B,WAAO,IAAI,kBACT,UACA,sBAAQ,uBACR,4BAAW;AAAA;AAAA,eAUF,iBACX,UACA,QACe;AACf,QAAI;AACF,YAAM,SAAS;AAAA,aACR,OAAP;AACA,UAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,cAAM,IAAI,MACR,kCAAmC,MAAgB;AAAA;AAGvD,uCAAQ,KACN,kCAAmC,MAAgB;AAAA;AAAA;AAAA,QAKnD,OAAO,KAA4B;AACvC,UAAM,KAAK,YACT,KAAK,SACF,WAAW,KAAK,MAChB,IAAI,IAAI,KACR,IAAI;AAAA,MACH,KAAK,IAAI;AAAA,MACT,KAAK,KAAK,UAAU;AAAA;AAAA;AAAA,QAKtB,WAA4C;AAChD,UAAM,OAAO,MAAM,KAAK,YACtB,KAAK,SAAS,WAAW,KAAK,MAAM;AAGtC,WAAO;AAAA,MACL,OAAO,KAAK,KAAK,IAAI;AAAQ,QAC3B,KAAK,IAAI;AAAA,QACT,WAAW,IAAI,WAAW;AAAA;AAAA;AAAA;AAAA,QAK1B,WAAW,MAA+B;AAE9C,eAAW,OAAO,MAAM;AACtB,YAAM,KAAK,YACT,KAAK,SAAS,WAAW,KAAK,MAAM,IAAI,KAAK;AAAA;AAAA;AAAA,QAqCrC,YAAe,WAAmC;AAC9D,UAAM,QAAQ,IAAI,QAAe,CAAC,GAAG,WACnC,WAAW,MAAM;AACf,aAAO,IAAI,MAAM,6BAA6B,KAAK;AAAA,OAClD,KAAK;AAEV,WAAO,QAAQ,KAAQ,CAAC,WAAW;AAAA;AAAA,QAMvB,SAAwB;AACpC,UAAM,KAAK,YAAY,KAAK,SAAS,WAAW,KAAK,MAAM,MAAM,GAAG;AAAA;AAAA;;gBC3HjD;AAAA,eAOR,WACX,QACA,SACmB;AAzCvB;AA0CI,UAAM,CAAE,QAAQ,YAAa,4BAAW;AAExC,UAAM,KAAK,OAAO,kBAAkB;AACpC,UAAM,WAAW,+BAAI,kBAAkB,gBAAtB,YAAqC;AAEtD,qCAAQ,KAAK,gBAAgB;AAE7B,QAAI,aAAa,YAAY;AAC3B,UAAI,CAAC,UAAU;AACb,cAAM,IAAI,MAAM;AAAA;AAGlB,aAAO,MAAM,iBAAiB,OAAO;AAAA,QACnC,UAAU,MAAM,SAAS;AAAA;AAAA;AAI7B,QAAI,aAAa,UAAU;AACzB,aAAO,IAAI;AAAA;AAGb,QAAI,aAAa,aAAa;AAC5B,YAAM,WAAW,yBAAI,UAAU;AAE/B,YAAM,WAAW,MAAM,kBAAkB,OAAO;AAAA,QAC9C,WAAW,qCAAU,kBAAkB;AAAA,QACvC,aAAa,qCAAU,kBAAkB;AAAA,QACzC,MAAM,qCAAU,kBAAkB;AAAA,QAClC,MAAM,qCAAU,kBAAkB;AAAA,QAClC,KAAK,qCAAU,mBAAmB;AAAA,QAClC,MAAM,qCAAU,kBAAkB;AAAA,QAClC,SAAS,qCAAU,kBAAkB;AAAA;AAGvC,YAAM,kBAAkB,iBAAiB,UAAU;AAEnD,aAAO;AAAA;AAGT,UAAM,IAAI,MAAM,8BAA8B;AAAA;AAAA;;4BCnCf;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,GACyC;AACzC,QAAM,SAASP;AAEf,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,UAAU,MAAM,UAAU,mBAAmB;AAEnD,QAAM,WAAW,MAAM,UAAU,WAAW,QAAQ,CAAE,QAAQ;AAC9D,QAAM,qBAAqB;AAE3B,QAAM,cAAc,IAAI,aAAa;AAAA,IACnC,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA,QAAQ,OAAO,MAAM,CAAE,WAAW;AAAA;AAEpC,QAAM,aAAa,IAAIQ,4BAAc,CAAE,cAAc;AAErD,QAAM,SAAS,OAAO,kBAAkB;AACxC,MAAI,QAAQ;AACV,WAAO,IAAIC,iCAAa;AAExB,WAAO,IAAIC,4BAAQ,CAAE,QAAQ,mBAAmB,OAAO,QAAQ;AAC/D,WAAO,IAAIC,6BAAS;AACpB,WAAO,IAAIA,6BAAS;AAAA,SACf;AACL,WAAO,IAAIF;AAAA;AAEb,SAAO,IAAIG,4BAAQ,WAAW,CAAE,UAAU;AAC1C,SAAO,IAAIA,4BAAQ;AAEnB,QAAM,uBAAuB;AAAA,OACxBC;AAAA,OACA;AAAA;AAEL,QAAM,kBAAkB,OAAO,UAAU;AACzC,QAAM,sBAAsB,gBAAgB;AAE5C,QAAM,kBAAkB,mBAAmB;AAE3C,aAAW,CAAC,YAAY,oBAAoB,OAAO,QACjD,uBACC;AACD,QAAI,oBAAoB,SAAS,aAAa;AAC5C,aAAO,KAAK,yBAAyB;AACrC,UAAI;AACF,cAAM,WAAW,gBAAgB;AAAA,UAC/B;AAAA,UACA,cAAc,CAAE,SAAS,SAAS,QAAQ;AAAA,UAC1C,QAAQ,gBAAgB,UAAU;AAAA,UAClC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAGF,cAAM,IAAIb;AAEV,UAAE,IAAI,UAAU,SAAS,MAAM,KAAK;AACpC,UAAE,IAAI,kBAAkB,SAAS,aAAa,KAAK;AACnD,UAAE,KAAK,kBAAkB,SAAS,aAAa,KAAK;AACpD,YAAI,SAAS,QAAQ;AACnB,YAAE,KAAK,WAAW,SAAS,OAAO,KAAK;AAAA;AAEzC,YAAI,SAAS,SAAS;AACpB,YAAE,IAAI,YAAY,SAAS,QAAQ,KAAK;AAAA;AAG1C,eAAO,IAAI,IAAI,cAAc;AAAA,eACtB,GAAP;AACA,2BAAY;AACZ,YAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,gBAAM,IAAI,MACR,wBAAwB,6BAA6B,EAAE;AAAA;AAI3D,eAAO,KAAK,YAAY,6BAA6B,EAAE;AAEvD,eAAO,IAAI,IAAI,cAAc,MAAM;AAEjC,gBAAM,IAAIpB,qBACR,iCAAiC,kFACb;AAAA;AAAA;AAAA,WAKrB;AACL,aAAO,IAAI,IAAI,cAAc,MAAM;AACjC,cAAM,IAAIA,qBACR,oCAAoC;AAAA;AAAA;AAAA;AAM5C,SAAO,IACL,iBAAiB;AAAA,IACf;AAAA,IACA,SAAS;AAAA;AAIb,SAAO,IAAI,eAAe,SAAO;AAC/B,UAAM,CAAE,YAAa,IAAI;AACzB,UAAM,IAAIA,qBAAc,0BAA0B;AAAA;AAGpD,SAAO;AAAA;4BAIP,QAC6B;AArK/B;AAsKE,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,CAAE,QAAQ,aAAc,IAAI,IAAI;AAEtC,QAAM,iBAAiB,OAAO,uBAC5B;AAGF,QAAM,wBACJ,uDAAgB,IACd,aAAW,IAAIkC,oBAAU,SAAS,CAAE,QAAQ,MAAM,YAAY,YADhE,YAEK;AAEP,SAAO,YAAU;AACf,QAAI,WAAW,WAAW;AACxB,aAAO;AAAA;AAET,WAAO,sBAAsB,KAAK,aAAW,QAAQ,MAAM;AAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/lib/passport/PassportStrategyHelper.ts","../src/lib/oauth/helpers.ts","../src/lib/oauth/OAuthEnvironmentHandler.ts","../src/lib/flow/authFlowHelpers.ts","../src/lib/oauth/OAuthAdapter.ts","../src/lib/catalog/CatalogIdentityClient.ts","../src/lib/catalog/helpers.ts","../src/providers/github/provider.ts","../src/providers/gitlab/provider.ts","../src/providers/google/provider.ts","../src/providers/microsoft/provider.ts","../src/providers/oauth2/provider.ts","../src/providers/okta/provider.ts","../src/providers/bitbucket/provider.ts","../src/providers/atlassian/strategy.ts","../src/providers/atlassian/provider.ts","../src/providers/aws-alb/provider.ts","../src/providers/oidc/provider.ts","../src/providers/saml/provider.ts","../src/providers/auth0/strategy.ts","../src/providers/auth0/provider.ts","../src/providers/onelogin/provider.ts","../src/providers/factories.ts","../src/identity/router.ts","../src/identity/IdentityClient.ts","../src/identity/TokenFactory.ts","../src/identity/DatabaseKeyStore.ts","../src/identity/MemoryKeyStore.ts","../src/identity/FirestoreKeyStore.ts","../src/identity/KeyStores.ts","../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport passport from 'passport';\nimport jwtDecoder from 'jwt-decode';\nimport { InternalOAuthError } from 'passport-oauth2';\n\nimport { PassportProfile } from './types';\nimport { ProfileInfo, RedirectInfo } from '../../providers/types';\n\nexport type PassportDoneCallback<Res, Private = never> = (\n err?: Error,\n response?: Res,\n privateInfo?: Private,\n) => void;\n\nexport const makeProfileInfo = (\n profile: PassportProfile,\n idToken?: string,\n): ProfileInfo => {\n let email: string | undefined = undefined;\n if (profile.emails && profile.emails.length > 0) {\n const [firstEmail] = profile.emails;\n email = firstEmail.value;\n }\n\n let picture: string | undefined = undefined;\n if (profile.avatarUrl) {\n picture = profile.avatarUrl;\n } else if (profile.photos && profile.photos.length > 0) {\n const [firstPhoto] = profile.photos;\n picture = firstPhoto.value;\n }\n\n let displayName: string | undefined =\n profile.displayName ?? profile.username ?? profile.id;\n\n if ((!email || !picture || !displayName) && idToken) {\n try {\n const decoded: Record<string, string> = jwtDecoder(idToken);\n if (!email && decoded.email) {\n email = decoded.email;\n }\n if (!picture && decoded.picture) {\n picture = decoded.picture;\n }\n if (!displayName && decoded.name) {\n displayName = decoded.name;\n }\n } catch (e) {\n throw new Error(`Failed to parse id token and get profile info, ${e}`);\n }\n }\n\n return {\n email,\n picture,\n displayName,\n };\n};\n\nexport const executeRedirectStrategy = async (\n req: express.Request,\n providerStrategy: passport.Strategy,\n options: Record<string, string>,\n): Promise<RedirectInfo> => {\n return new Promise(resolve => {\n const strategy = Object.create(providerStrategy);\n strategy.redirect = (url: string, status?: number) => {\n resolve({ url, status: status ?? undefined });\n };\n\n strategy.authenticate(req, { ...options });\n });\n};\n\nexport const executeFrameHandlerStrategy = async <Result, PrivateInfo = never>(\n req: express.Request,\n providerStrategy: passport.Strategy,\n) => {\n return new Promise<{ result: Result; privateInfo: PrivateInfo }>(\n (resolve, reject) => {\n const strategy = Object.create(providerStrategy);\n strategy.success = (result: any, privateInfo: any) => {\n resolve({ result, privateInfo });\n };\n strategy.fail = (\n info: { type: 'success' | 'error'; message?: string },\n // _status: number,\n ) => {\n reject(new Error(`Authentication rejected, ${info.message ?? ''}`));\n };\n strategy.error = (error: InternalOAuthError) => {\n let message = `Authentication failed, ${error.message}`;\n\n if (error.oauthError?.data) {\n try {\n const errorData = JSON.parse(error.oauthError.data);\n\n if (errorData.message) {\n message += ` - ${errorData.message}`;\n }\n } catch (parseError) {\n message += ` - ${error.oauthError}`;\n }\n }\n\n reject(new Error(message));\n };\n strategy.redirect = () => {\n reject(new Error('Unexpected redirect'));\n };\n strategy.authenticate(req, {});\n },\n );\n};\n\ntype RefreshTokenResponse = {\n /**\n * An access token issued for the signed in user.\n */\n accessToken: string;\n /**\n * Optionally, the server can issue a new Refresh Token for the user\n */\n refreshToken?: string;\n params: any;\n};\n\nexport const executeRefreshTokenStrategy = async (\n providerStrategy: passport.Strategy,\n refreshToken: string,\n scope: string,\n): Promise<RefreshTokenResponse> => {\n return new Promise((resolve, reject) => {\n const anyStrategy = providerStrategy as any;\n const OAuth2 = anyStrategy._oauth2.constructor;\n const oauth2 = new OAuth2(\n anyStrategy._oauth2._clientId,\n anyStrategy._oauth2._clientSecret,\n anyStrategy._oauth2._baseSite,\n anyStrategy._oauth2._authorizeUrl,\n anyStrategy._refreshURL || anyStrategy._oauth2._accessTokenUrl,\n anyStrategy._oauth2._customHeaders,\n );\n\n oauth2.getOAuthAccessToken(\n refreshToken,\n {\n scope,\n grant_type: 'refresh_token',\n },\n (\n err: Error | null,\n accessToken: string,\n newRefreshToken: string,\n params: any,\n ) => {\n if (err) {\n reject(new Error(`Failed to refresh access token ${err.toString()}`));\n }\n if (!accessToken) {\n reject(\n new Error(\n `Failed to refresh access token, no access token received`,\n ),\n );\n }\n\n resolve({\n accessToken,\n refreshToken: newRefreshToken,\n params,\n });\n },\n );\n });\n};\n\ntype ProviderStrategy = {\n userProfile(accessToken: string, callback: Function): void;\n};\n\nexport const executeFetchUserProfileStrategy = async (\n providerStrategy: passport.Strategy,\n accessToken: string,\n): Promise<PassportProfile> => {\n return new Promise((resolve, reject) => {\n const anyStrategy = providerStrategy as unknown as ProviderStrategy;\n anyStrategy.userProfile(\n accessToken,\n (error: Error, rawProfile: PassportProfile) => {\n if (error) {\n reject(error);\n } else {\n resolve(rawProfile);\n }\n },\n );\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport { OAuthState } from './types';\nimport pickBy from 'lodash/pickBy';\n\nexport const readState = (stateString: string): OAuthState => {\n const state = Object.fromEntries(\n new URLSearchParams(Buffer.from(stateString, 'hex').toString('utf-8')),\n );\n if (\n !state.nonce ||\n !state.env ||\n state.nonce?.length === 0 ||\n state.env?.length === 0\n ) {\n throw Error(`Invalid state passed via request`);\n }\n\n return state as OAuthState;\n};\n\nexport const encodeState = (state: OAuthState): string => {\n const stateString = new URLSearchParams(\n pickBy<string>(state, value => value !== undefined),\n ).toString();\n\n return Buffer.from(stateString, 'utf-8').toString('hex');\n};\n\nexport const verifyNonce = (req: express.Request, providerId: string) => {\n const cookieNonce = req.cookies[`${providerId}-nonce`];\n const state: OAuthState = readState(req.query.state?.toString() ?? '');\n const stateNonce = state.nonce;\n\n if (!cookieNonce) {\n throw new Error('Auth response is missing cookie nonce');\n }\n if (stateNonce.length === 0) {\n throw new Error('Auth response is missing state nonce');\n }\n if (cookieNonce !== stateNonce) {\n throw new Error('Invalid nonce');\n }\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport { Config } from '@backstage/config';\nimport { InputError } from '@backstage/errors';\nimport { readState } from './helpers';\nimport { AuthProviderRouteHandlers } from '../../providers/types';\n\nexport class OAuthEnvironmentHandler implements AuthProviderRouteHandlers {\n static mapConfig(\n config: Config,\n factoryFunc: (envConfig: Config) => AuthProviderRouteHandlers,\n ) {\n const envs = config.keys();\n const handlers = new Map<string, AuthProviderRouteHandlers>();\n\n for (const env of envs) {\n const envConfig = config.getConfig(env);\n const handler = factoryFunc(envConfig);\n handlers.set(env, handler);\n }\n\n return new OAuthEnvironmentHandler(handlers);\n }\n\n constructor(\n private readonly handlers: Map<string, AuthProviderRouteHandlers>,\n ) {}\n\n async start(req: express.Request, res: express.Response): Promise<void> {\n const provider = this.getProviderForEnv(req, res);\n await provider?.start(req, res);\n }\n\n async frameHandler(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const provider = this.getProviderForEnv(req, res);\n await provider?.frameHandler(req, res);\n }\n\n async refresh(req: express.Request, res: express.Response): Promise<void> {\n const provider = this.getProviderForEnv(req, res);\n await provider?.refresh?.(req, res);\n }\n\n async logout(req: express.Request, res: express.Response): Promise<void> {\n const provider = this.getProviderForEnv(req, res);\n await provider?.logout?.(req, res);\n }\n\n private getRequestFromEnv(req: express.Request): string | undefined {\n const reqEnv = req.query.env?.toString();\n if (reqEnv) {\n return reqEnv;\n }\n const stateParams = req.query.state?.toString();\n if (!stateParams) {\n return undefined;\n }\n const env = readState(stateParams).env;\n return env;\n }\n\n private getProviderForEnv(\n req: express.Request,\n res: express.Response,\n ): AuthProviderRouteHandlers | undefined {\n const env: string | undefined = this.getRequestFromEnv(req);\n\n if (!env) {\n throw new InputError(`Must specify 'env' query to select environment`);\n }\n\n if (!this.handlers.has(env)) {\n res.status(404).send(\n `Missing configuration.\n <br>\n <br>\n For this flow to work you need to supply a valid configuration for the \"${env}\" environment of provider.`,\n );\n return undefined;\n }\n\n return this.handlers.get(env);\n }\n}\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport crypto from 'crypto';\nimport { WebMessageResponse } from './types';\n\nexport const safelyEncodeURIComponent = (value: string) => {\n // Note the g at the end of the regex; all occurrences of single quotes must\n // be replaced, which encodeURIComponent does not do itself by default\n return encodeURIComponent(value).replace(/'/g, '%27');\n};\n\nexport const postMessageResponse = (\n res: express.Response,\n appOrigin: string,\n response: WebMessageResponse,\n) => {\n const jsonData = JSON.stringify(response);\n const base64Data = safelyEncodeURIComponent(jsonData);\n const base64Origin = safelyEncodeURIComponent(appOrigin);\n\n // NOTE: It is absolutely imperative that we use the safe encoder above, to\n // be sure that the js code below does not allow the injection of malicious\n // data.\n\n // TODO: Make target app origin configurable globally\n\n //\n // postMessage fails silently if the targetOrigin is disallowed.\n // So 2 postMessages are sent from the popup to the parent window.\n // First, the origin being used to post the actual authorization response is\n // shared with the parent window with a postMessage with targetOrigin '*'.\n // Second, the actual authorization response is sent with the app origin\n // as the targetOrigin.\n // If the first message was received but the actual auth response was\n // never received, the event listener can conclude that targetOrigin\n // was disallowed, indicating potential misconfiguration.\n //\n const script = `\n var authResponse = decodeURIComponent('${base64Data}');\n var origin = decodeURIComponent('${base64Origin}');\n var originInfo = {'type': 'config_info', 'targetOrigin': origin};\n (window.opener || window.parent).postMessage(originInfo, '*');\n (window.opener || window.parent).postMessage(JSON.parse(authResponse), origin);\n setTimeout(() => {\n window.close();\n }, 100); // same as the interval of the core-app-api lib/loginPopup.ts (to address race conditions)\n `;\n const hash = crypto.createHash('sha256').update(script).digest('base64');\n\n res.setHeader('Content-Type', 'text/html');\n res.setHeader('X-Frame-Options', 'sameorigin');\n res.setHeader('Content-Security-Policy', `script-src 'sha256-${hash}'`);\n res.end(`<html><body><script>${script}</script></body></html>`);\n};\n\nexport const ensuresXRequestedWith = (req: express.Request) => {\n const requiredHeader = req.header('X-Requested-With');\n if (!requiredHeader || requiredHeader !== 'XMLHttpRequest') {\n return false;\n }\n return true;\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport crypto from 'crypto';\nimport { URL } from 'url';\nimport {\n AuthProviderRouteHandlers,\n BackstageIdentity,\n AuthProviderConfig,\n} from '../../providers/types';\nimport { InputError, isError, NotAllowedError } from '@backstage/errors';\nimport { TokenIssuer } from '../../identity/types';\nimport { readState, verifyNonce } from './helpers';\nimport { postMessageResponse, ensuresXRequestedWith } from '../flow';\nimport {\n OAuthHandlers,\n OAuthStartRequest,\n OAuthRefreshRequest,\n OAuthState,\n} from './types';\n\nexport const THOUSAND_DAYS_MS = 1000 * 24 * 60 * 60 * 1000;\nexport const TEN_MINUTES_MS = 600 * 1000;\n\nexport type Options = {\n providerId: string;\n secure: boolean;\n disableRefresh?: boolean;\n persistScopes?: boolean;\n cookieDomain: string;\n cookiePath: string;\n appOrigin: string;\n tokenIssuer: TokenIssuer;\n isOriginAllowed: (origin: string) => boolean;\n};\n\nexport class OAuthAdapter implements AuthProviderRouteHandlers {\n static fromConfig(\n config: AuthProviderConfig,\n handlers: OAuthHandlers,\n options: Pick<\n Options,\n 'providerId' | 'persistScopes' | 'disableRefresh' | 'tokenIssuer'\n >,\n ): OAuthAdapter {\n const { origin: appOrigin } = new URL(config.appUrl);\n const secure = config.baseUrl.startsWith('https://');\n const url = new URL(config.baseUrl);\n const cookiePath = `${url.pathname}/${options.providerId}`;\n return new OAuthAdapter(handlers, {\n ...options,\n appOrigin,\n cookieDomain: url.hostname,\n cookiePath,\n secure,\n isOriginAllowed: config.isOriginAllowed,\n });\n }\n\n constructor(\n private readonly handlers: OAuthHandlers,\n private readonly options: Options,\n ) {}\n\n async start(req: express.Request, res: express.Response): Promise<void> {\n // retrieve scopes from request\n const scope = req.query.scope?.toString() ?? '';\n const env = req.query.env?.toString();\n const origin = req.query.origin?.toString();\n\n if (!env) {\n throw new InputError('No env provided in request query parameters');\n }\n\n if (this.options.persistScopes) {\n this.setScopesCookie(res, scope);\n }\n\n const nonce = crypto.randomBytes(16).toString('base64');\n // set a nonce cookie before redirecting to oauth provider\n this.setNonceCookie(res, nonce);\n\n const state = { nonce, env, origin };\n const forwardReq = Object.assign(req, { scope, state });\n\n const { url, status } = await this.handlers.start(\n forwardReq as OAuthStartRequest,\n );\n\n res.statusCode = status || 302;\n res.setHeader('Location', url);\n res.setHeader('Content-Length', '0');\n res.end();\n }\n\n async frameHandler(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n let appOrigin = this.options.appOrigin;\n\n try {\n const state: OAuthState = readState(req.query.state?.toString() ?? '');\n\n if (state.origin) {\n try {\n appOrigin = new URL(state.origin).origin;\n } catch {\n throw new NotAllowedError('App origin is invalid, failed to parse');\n }\n if (!this.options.isOriginAllowed(appOrigin)) {\n throw new NotAllowedError(`Origin '${appOrigin}' is not allowed`);\n }\n }\n\n // verify nonce cookie and state cookie on callback\n verifyNonce(req, this.options.providerId);\n\n const { response, refreshToken } = await this.handlers.handler(req);\n\n if (this.options.persistScopes) {\n const grantedScopes = this.getScopesFromCookie(\n req,\n this.options.providerId,\n );\n response.providerInfo.scope = grantedScopes;\n }\n\n if (refreshToken && !this.options.disableRefresh) {\n // set new refresh token\n this.setRefreshTokenCookie(res, refreshToken);\n }\n\n await this.populateIdentity(response.backstageIdentity);\n\n // post message back to popup if successful\n return postMessageResponse(res, appOrigin, {\n type: 'authorization_response',\n response,\n });\n } catch (error) {\n const { name, message } = isError(error)\n ? error\n : new Error('Encountered invalid error'); // Being a bit safe and not forwarding the bad value\n // post error message back to popup if failure\n return postMessageResponse(res, appOrigin, {\n type: 'authorization_response',\n error: { name, message },\n });\n }\n }\n\n async logout(req: express.Request, res: express.Response): Promise<void> {\n if (!ensuresXRequestedWith(req)) {\n res.status(401).send('Invalid X-Requested-With header');\n return;\n }\n\n // remove refresh token cookie if it is set\n this.removeRefreshTokenCookie(res);\n\n res.status(200).send('logout!');\n }\n\n async refresh(req: express.Request, res: express.Response): Promise<void> {\n if (!ensuresXRequestedWith(req)) {\n res.status(401).send('Invalid X-Requested-With header');\n return;\n }\n\n if (!this.handlers.refresh || this.options.disableRefresh) {\n res\n .status(400)\n .send(\n `Refresh token not supported for provider: ${this.options.providerId}`,\n );\n return;\n }\n\n try {\n const refreshToken =\n req.cookies[`${this.options.providerId}-refresh-token`];\n\n // throw error if refresh token is missing in the request\n if (!refreshToken) {\n throw new Error('Missing session cookie');\n }\n\n const scope = req.query.scope?.toString() ?? '';\n\n const forwardReq = Object.assign(req, { scope, refreshToken });\n\n // get new access_token\n const response = await this.handlers.refresh(\n forwardReq as OAuthRefreshRequest,\n );\n\n await this.populateIdentity(response.backstageIdentity);\n\n if (\n response.providerInfo.refreshToken &&\n response.providerInfo.refreshToken !== refreshToken\n ) {\n this.setRefreshTokenCookie(res, response.providerInfo.refreshToken);\n }\n\n res.status(200).json(response);\n } catch (error) {\n res.status(401).send(String(error));\n }\n }\n\n /**\n * If the response from the OAuth provider includes a Backstage identity, we\n * make sure it's populated with all the information we can derive from the user ID.\n */\n private async populateIdentity(identity?: BackstageIdentity) {\n if (!identity) {\n return;\n }\n\n if (!identity.idToken) {\n identity.idToken = await this.options.tokenIssuer.issueToken({\n claims: { sub: identity.id },\n });\n }\n }\n\n private setNonceCookie = (res: express.Response, nonce: string) => {\n res.cookie(`${this.options.providerId}-nonce`, nonce, {\n maxAge: TEN_MINUTES_MS,\n secure: this.options.secure,\n sameSite: 'lax',\n domain: this.options.cookieDomain,\n path: `${this.options.cookiePath}/handler`,\n httpOnly: true,\n });\n };\n\n private setScopesCookie = (res: express.Response, scope: string) => {\n res.cookie(`${this.options.providerId}-scope`, scope, {\n maxAge: TEN_MINUTES_MS,\n secure: this.options.secure,\n sameSite: 'lax',\n domain: this.options.cookieDomain,\n path: `${this.options.cookiePath}/handler`,\n httpOnly: true,\n });\n };\n\n private getScopesFromCookie = (req: express.Request, providerId: string) => {\n return req.cookies[`${providerId}-scope`];\n };\n\n private setRefreshTokenCookie = (\n res: express.Response,\n refreshToken: string,\n ) => {\n res.cookie(`${this.options.providerId}-refresh-token`, refreshToken, {\n maxAge: THOUSAND_DAYS_MS,\n secure: this.options.secure,\n sameSite: 'lax',\n domain: this.options.cookieDomain,\n path: this.options.cookiePath,\n httpOnly: true,\n });\n };\n\n private removeRefreshTokenCookie = (res: express.Response) => {\n res.cookie(`${this.options.providerId}-refresh-token`, '', {\n maxAge: 0,\n secure: this.options.secure,\n sameSite: 'lax',\n domain: this.options.cookieDomain,\n path: this.options.cookiePath,\n httpOnly: true,\n });\n };\n}\n","/*\n * Copyright 2020 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 */\n\nimport { Logger } from 'winston';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport {\n EntityName,\n parseEntityRef,\n RELATION_MEMBER_OF,\n stringifyEntityRef,\n UserEntity,\n} from '@backstage/catalog-model';\nimport { TokenIssuer } from '../../identity';\n\ntype UserQuery = {\n annotations: Record<string, string>;\n};\n\ntype MemberClaimQuery = {\n entityRefs: string[];\n logger?: Logger;\n};\n\n/**\n * A catalog client tailored for reading out identity data from the catalog.\n */\nexport class CatalogIdentityClient {\n private readonly catalogApi: CatalogApi;\n private readonly tokenIssuer: TokenIssuer;\n\n constructor(options: { catalogApi: CatalogApi; tokenIssuer: TokenIssuer }) {\n this.catalogApi = options.catalogApi;\n this.tokenIssuer = options.tokenIssuer;\n }\n\n /**\n * Looks up a single user using a query.\n *\n * Throws a NotFoundError or ConflictError if 0 or multiple users are found.\n */\n async findUser(query: UserQuery): Promise<UserEntity> {\n const filter: Record<string, string> = {\n kind: 'user',\n };\n for (const [key, value] of Object.entries(query.annotations)) {\n filter[`metadata.annotations.${key}`] = value;\n }\n\n // TODO(Rugvip): cache the token\n const token = await this.tokenIssuer.issueToken({\n claims: { sub: 'backstage.io/auth-backend' },\n });\n const { items } = await this.catalogApi.getEntities({ filter }, { token });\n\n if (items.length !== 1) {\n if (items.length > 1) {\n throw new ConflictError('User lookup resulted in multiple matches');\n } else {\n throw new NotFoundError('User not found');\n }\n }\n\n return items[0] as UserEntity;\n }\n\n /**\n * Resolve additional entity claims from the catalog, using the passed-in entity names. Designed\n * to be used within a `signInResolver` where additional entity claims might be provided, but\n * group membership and transient group membership lean on imported catalog relations.\n *\n * Returns a superset of the entity names that can be passed directly to `issueToken` as `ent`.\n */\n async resolveCatalogMembership({\n entityRefs,\n logger,\n }: MemberClaimQuery): Promise<string[]> {\n const resolvedEntityRefs = entityRefs\n .map((ref: string) => {\n try {\n const parsedRef = parseEntityRef(ref.toLocaleLowerCase('en-US'), {\n defaultKind: 'user',\n defaultNamespace: 'default',\n });\n return parsedRef;\n } catch {\n logger?.warn(`Failed to parse entityRef from ${ref}, ignoring`);\n return null;\n }\n })\n .filter((ref): ref is EntityName => ref !== null);\n\n const filter = resolvedEntityRefs.map(ref => ({\n kind: ref.kind,\n 'metadata.namespace': ref.namespace,\n 'metadata.name': ref.name,\n }));\n const entities = await this.catalogApi\n .getEntities({ filter })\n .then(r => r.items);\n\n if (entityRefs.length !== entities.length) {\n const foundEntityNames = entities.map(stringifyEntityRef);\n const missingEntityNames = resolvedEntityRefs\n .map(stringifyEntityRef)\n .filter(s => !foundEntityNames.includes(s));\n logger?.debug(`Entities not found for refs ${missingEntityNames.join()}`);\n }\n\n const memberOf = entities.flatMap(\n e =>\n e!.relations\n ?.filter(r => r.type === RELATION_MEMBER_OF)\n .map(r => r.target) ?? [],\n );\n\n const newEntityRefs = [\n ...new Set(resolvedEntityRefs.concat(memberOf).map(stringifyEntityRef)),\n ];\n\n logger?.debug(`Found catalog membership: ${newEntityRefs.join()}`);\n return newEntityRefs;\n }\n}\n","/*\n * Copyright 2021 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 */\n\nimport {\n RELATION_MEMBER_OF,\n stringifyEntityRef,\n UserEntity,\n} from '@backstage/catalog-model';\nimport { TokenParams } from '../../identity';\n\nexport function getEntityClaims(entity: UserEntity): TokenParams['claims'] {\n const userRef = stringifyEntityRef(entity);\n\n const membershipRefs =\n entity.relations\n ?.filter(\n r =>\n r.type === RELATION_MEMBER_OF &&\n r.target.kind.toLocaleLowerCase('en-US') === 'group',\n )\n .map(r => stringifyEntityRef(r.target)) ?? [];\n\n return {\n sub: userRef,\n ent: [userRef, ...membershipRefs],\n };\n}\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport { Logger } from 'winston';\nimport { Profile as PassportProfile } from 'passport';\nimport { Strategy as GithubStrategy } from 'passport-github2';\nimport {\n executeFetchUserProfileStrategy,\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n RedirectInfo,\n AuthProviderFactory,\n AuthHandler,\n SignInResolver,\n StateEncoder,\n} from '../types';\nimport {\n OAuthAdapter,\n OAuthProviderOptions,\n OAuthHandlers,\n OAuthEnvironmentHandler,\n OAuthStartRequest,\n encodeState,\n OAuthRefreshRequest,\n OAuthResponse,\n} from '../../lib/oauth';\nimport { CatalogIdentityClient } from '../../lib/catalog';\nimport { TokenIssuer } from '../../identity';\n\ntype PrivateInfo = {\n refreshToken?: string;\n};\n\nexport type GithubOAuthResult = {\n fullProfile: PassportProfile;\n params: {\n scope: string;\n expires_in?: string;\n refresh_token_expires_in?: string;\n };\n accessToken: string;\n refreshToken?: string;\n};\n\nexport type GithubAuthProviderOptions = OAuthProviderOptions & {\n tokenUrl?: string;\n userProfileUrl?: string;\n authorizationUrl?: string;\n signInResolver?: SignInResolver<GithubOAuthResult>;\n authHandler: AuthHandler<GithubOAuthResult>;\n stateEncoder: StateEncoder;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n logger: Logger;\n};\n\nexport class GithubAuthProvider implements OAuthHandlers {\n private readonly _strategy: GithubStrategy;\n private readonly signInResolver?: SignInResolver<GithubOAuthResult>;\n private readonly authHandler: AuthHandler<GithubOAuthResult>;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n private readonly stateEncoder: StateEncoder;\n\n constructor(options: GithubAuthProviderOptions) {\n this.signInResolver = options.signInResolver;\n this.authHandler = options.authHandler;\n this.stateEncoder = options.stateEncoder;\n this.tokenIssuer = options.tokenIssuer;\n this.catalogIdentityClient = options.catalogIdentityClient;\n this.logger = options.logger;\n this._strategy = new GithubStrategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n tokenURL: options.tokenUrl,\n userProfileURL: options.userProfileUrl,\n authorizationURL: options.authorizationUrl,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: any,\n done: PassportDoneCallback<GithubOAuthResult, PrivateInfo>,\n ) => {\n done(undefined, { fullProfile, params, accessToken }, { refreshToken });\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n scope: req.scope,\n state: (await this.stateEncoder(req)).encodedState,\n });\n }\n\n async handler(req: express.Request) {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n GithubOAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n return {\n response: await this.handleResult(result),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const {\n accessToken,\n refreshToken: newRefreshToken,\n params,\n } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: newRefreshToken,\n });\n }\n\n private async handleResult(result: GithubOAuthResult) {\n const { profile } = await this.authHandler(result);\n\n const expiresInStr = result.params.expires_in;\n const response: OAuthResponse = {\n providerInfo: {\n accessToken: result.accessToken,\n refreshToken: result.refreshToken, // GitHub expires the old refresh token when used\n scope: result.params.scope,\n expiresInSeconds:\n expiresInStr === undefined ? undefined : Number(expiresInStr),\n },\n profile,\n };\n\n if (this.signInResolver) {\n response.backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n }\n\n return response;\n }\n}\n\nexport const githubDefaultSignInResolver: SignInResolver<GithubOAuthResult> =\n async (info, ctx) => {\n const { fullProfile } = info.result;\n\n const userId = fullProfile.username || fullProfile.id;\n\n const token = await ctx.tokenIssuer.issueToken({\n claims: { sub: userId, ent: [`user:default/${userId}`] },\n });\n\n return { id: userId, token };\n };\n\nexport type GithubProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<GithubOAuthResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n signIn?: {\n /**\n * Maps an auth result to a Backstage identity for the user.\n */\n resolver?: SignInResolver<GithubOAuthResult>;\n };\n\n /**\n * The state encoder used to encode the 'state' parameter on the OAuth request.\n *\n * It should return a string that takes the state params (from the request), url encodes the params\n * and finally base64 encodes them.\n *\n * Providing your own stateEncoder will allow you to add addition parameters to the state field.\n *\n * It is typed as follows:\n * export type StateEncoder = (input: OAuthState) => Promise<{encodedState: string}>;\n *\n * Note: the stateEncoder must encode a 'nonce' value and an 'env' value. Without this, the OAuth flow will fail\n * (These two values will be set by the req.state by default)\n *\n * For more information, please see the helper module in ../../oauth/helpers #readState\n */\n stateEncoder?: StateEncoder;\n};\n\nexport const createGithubProvider = (\n options?: GithubProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const enterpriseInstanceUrl = envConfig.getOptionalString(\n 'enterpriseInstanceUrl',\n );\n const customCallbackUrl = envConfig.getOptionalString('callbackUrl');\n const authorizationUrl = enterpriseInstanceUrl\n ? `${enterpriseInstanceUrl}/login/oauth/authorize`\n : undefined;\n const tokenUrl = enterpriseInstanceUrl\n ? `${enterpriseInstanceUrl}/login/oauth/access_token`\n : undefined;\n const userProfileUrl = enterpriseInstanceUrl\n ? `${enterpriseInstanceUrl}/api/v3/user`\n : undefined;\n const callbackUrl =\n customCallbackUrl ||\n `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<GithubOAuthResult> = options?.authHandler\n ? options.authHandler\n : async ({ fullProfile }) => ({\n profile: makeProfileInfo(fullProfile),\n });\n\n const signInResolverFn =\n options?.signIn?.resolver ?? githubDefaultSignInResolver;\n\n const signInResolver: SignInResolver<GithubOAuthResult> = info =>\n signInResolverFn(info, {\n catalogIdentityClient,\n tokenIssuer,\n logger,\n });\n\n const stateEncoder: StateEncoder =\n options?.stateEncoder ??\n (async (req: OAuthStartRequest): Promise<{ encodedState: string }> => {\n return { encodedState: encodeState(req.state) };\n });\n\n const provider = new GithubAuthProvider({\n clientId,\n clientSecret,\n callbackUrl,\n tokenUrl,\n userProfileUrl,\n authorizationUrl,\n signInResolver,\n authHandler,\n tokenIssuer,\n catalogIdentityClient,\n stateEncoder,\n logger,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n persistScopes: true,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport { Strategy as GitlabStrategy } from 'passport-gitlab2';\nimport { Logger } from 'winston';\n\nimport {\n executeRedirectStrategy,\n executeFrameHandlerStrategy,\n executeRefreshTokenStrategy,\n executeFetchUserProfileStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n RedirectInfo,\n AuthProviderFactory,\n SignInResolver,\n AuthHandler,\n} from '../types';\nimport {\n OAuthAdapter,\n OAuthProviderOptions,\n OAuthHandlers,\n OAuthResponse,\n OAuthEnvironmentHandler,\n OAuthStartRequest,\n OAuthRefreshRequest,\n encodeState,\n OAuthResult,\n} from '../../lib/oauth';\nimport { TokenIssuer } from '../../identity';\nimport { CatalogIdentityClient } from '../../lib/catalog';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\nexport type GitlabAuthProviderOptions = OAuthProviderOptions & {\n baseUrl: string;\n signInResolver?: SignInResolver<OAuthResult>;\n authHandler: AuthHandler<OAuthResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n logger: Logger;\n};\n\nexport const gitlabDefaultSignInResolver: SignInResolver<OAuthResult> = async (\n info,\n ctx,\n) => {\n const { profile, result } = info;\n\n let id = result.fullProfile.id;\n\n if (profile.email) {\n id = profile.email.split('@')[0];\n }\n\n const token = await ctx.tokenIssuer.issueToken({\n claims: { sub: id, ent: [`user:default/${id}`] },\n });\n\n return { id, token };\n};\n\nexport const gitlabDefaultAuthHandler: AuthHandler<OAuthResult> = async ({\n fullProfile,\n params,\n}) => ({\n profile: makeProfileInfo(fullProfile, params.id_token),\n});\n\nexport class GitlabAuthProvider implements OAuthHandlers {\n private readonly _strategy: GitlabStrategy;\n private readonly signInResolver?: SignInResolver<OAuthResult>;\n private readonly authHandler: AuthHandler<OAuthResult>;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n\n constructor(options: GitlabAuthProviderOptions) {\n this.catalogIdentityClient = options.catalogIdentityClient;\n this.logger = options.logger;\n this.tokenIssuer = options.tokenIssuer;\n this.authHandler = options.authHandler;\n this.signInResolver = options.signInResolver;\n\n this._strategy = new GitlabStrategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n baseURL: options.baseUrl,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: any,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(\n undefined,\n { fullProfile, params, accessToken },\n {\n refreshToken,\n },\n );\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n scope: req.scope,\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n return {\n response: await this.handleResult(result),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const {\n accessToken,\n refreshToken: newRefreshToken,\n params,\n } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: newRefreshToken,\n });\n }\n\n private async handleResult(result: OAuthResult): Promise<OAuthResponse> {\n const { profile } = await this.authHandler(result);\n\n const response: OAuthResponse = {\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n refreshToken: result.refreshToken, // GitLab expires the old refresh token when used\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n profile,\n };\n\n if (this.signInResolver) {\n response.backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n }\n\n return response;\n }\n}\n\nexport type GitlabProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<OAuthResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n /**\n * Maps an auth result to a Backstage identity for the user.\n *\n * Set to `'email'` to use the default email-based sign in resolver, which will search\n * the catalog for a single user entity that has a matching `microsoft.com/email` annotation.\n */\n signIn?: {\n resolver?: SignInResolver<OAuthResult>;\n };\n};\n\nexport const createGitlabProvider = (\n options?: GitlabProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const audience = envConfig.getOptionalString('audience');\n const baseUrl = audience || 'https://gitlab.com';\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<OAuthResult> =\n options?.authHandler ?? gitlabDefaultAuthHandler;\n\n const signInResolverFn =\n options?.signIn?.resolver ?? gitlabDefaultSignInResolver;\n\n const signInResolver: SignInResolver<OAuthResult> = info =>\n signInResolverFn(info, {\n catalogIdentityClient,\n tokenIssuer,\n logger,\n });\n\n const provider = new GitlabAuthProvider({\n clientId,\n clientSecret,\n callbackUrl,\n baseUrl,\n authHandler,\n signInResolver,\n catalogIdentityClient,\n logger,\n tokenIssuer,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: false,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport passport from 'passport';\nimport { Strategy as GoogleStrategy } from 'passport-google-oauth20';\nimport { TokenIssuer } from '../../identity/types';\nimport { CatalogIdentityClient, getEntityClaims } from '../../lib/catalog';\nimport {\n encodeState,\n OAuthAdapter,\n OAuthEnvironmentHandler,\n OAuthHandlers,\n OAuthProviderOptions,\n OAuthRefreshRequest,\n OAuthResponse,\n OAuthResult,\n OAuthStartRequest,\n} from '../../lib/oauth';\nimport {\n executeFetchUserProfileStrategy,\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n AuthProviderFactory,\n AuthHandler,\n RedirectInfo,\n SignInResolver,\n} from '../types';\nimport { Logger } from 'winston';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\ntype Options = OAuthProviderOptions & {\n signInResolver?: SignInResolver<OAuthResult>;\n authHandler: AuthHandler<OAuthResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n logger: Logger;\n};\n\nexport class GoogleAuthProvider implements OAuthHandlers {\n private readonly _strategy: GoogleStrategy;\n private readonly signInResolver?: SignInResolver<OAuthResult>;\n private readonly authHandler: AuthHandler<OAuthResult>;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n\n constructor(options: Options) {\n this.signInResolver = options.signInResolver;\n this.authHandler = options.authHandler;\n this.tokenIssuer = options.tokenIssuer;\n this.catalogIdentityClient = options.catalogIdentityClient;\n this.logger = options.logger;\n this._strategy = new GoogleStrategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n // We need passReqToCallback set to false to get params, but there's\n // no matching type signature for that, so instead behold this beauty\n passReqToCallback: false as true,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(\n undefined,\n {\n fullProfile,\n params,\n accessToken,\n refreshToken,\n },\n {\n refreshToken,\n },\n );\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n accessType: 'offline',\n prompt: 'consent',\n scope: req.scope,\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n return {\n response: await this.handleResult(result),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const { accessToken, params } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: req.refreshToken,\n });\n }\n\n private async handleResult(result: OAuthResult) {\n const { profile } = await this.authHandler(result);\n\n const response: OAuthResponse = {\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n profile,\n };\n\n if (this.signInResolver) {\n response.backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n }\n\n return response;\n }\n}\n\nexport const googleEmailSignInResolver: SignInResolver<OAuthResult> = async (\n info,\n ctx,\n) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error('Google profile contained no email');\n }\n\n const entity = await ctx.catalogIdentityClient.findUser({\n annotations: {\n 'google.com/email': profile.email,\n },\n });\n\n const claims = getEntityClaims(entity);\n const token = await ctx.tokenIssuer.issueToken({ claims });\n\n return { id: entity.metadata.name, entity, token };\n};\n\nconst googleDefaultSignInResolver: SignInResolver<OAuthResult> = async (\n info,\n ctx,\n) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error('Google profile contained no email');\n }\n\n let userId: string;\n try {\n const entity = await ctx.catalogIdentityClient.findUser({\n annotations: {\n 'google.com/email': profile.email,\n },\n });\n userId = entity.metadata.name;\n } catch (error) {\n ctx.logger.warn(\n `Failed to look up user, ${error}, falling back to allowing login based on email pattern, this will probably break in the future`,\n );\n userId = profile.email.split('@')[0];\n }\n\n const token = await ctx.tokenIssuer.issueToken({\n claims: { sub: userId, ent: [`user:default/${userId}`] },\n });\n\n return { id: userId, token };\n};\n\nexport type GoogleProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<OAuthResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n signIn?: {\n /**\n * Maps an auth result to a Backstage identity for the user.\n */\n resolver?: SignInResolver<OAuthResult>;\n };\n};\n\nexport const createGoogleProvider = (\n options?: GoogleProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<OAuthResult> = options?.authHandler\n ? options.authHandler\n : async ({ fullProfile, params }) => ({\n profile: makeProfileInfo(fullProfile, params.id_token),\n });\n\n const signInResolverFn =\n options?.signIn?.resolver ?? googleDefaultSignInResolver;\n\n const signInResolver: SignInResolver<OAuthResult> = info =>\n signInResolverFn(info, {\n catalogIdentityClient,\n tokenIssuer,\n logger,\n });\n\n const provider = new GoogleAuthProvider({\n clientId,\n clientSecret,\n callbackUrl,\n signInResolver,\n authHandler,\n tokenIssuer,\n catalogIdentityClient,\n logger,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: false,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport passport from 'passport';\nimport { Strategy as MicrosoftStrategy } from 'passport-microsoft';\nimport { TokenIssuer } from '../../identity/types';\nimport { CatalogIdentityClient, getEntityClaims } from '../../lib/catalog';\nimport {\n encodeState,\n OAuthAdapter,\n OAuthEnvironmentHandler,\n OAuthHandlers,\n OAuthProviderOptions,\n OAuthRefreshRequest,\n OAuthResponse,\n OAuthResult,\n OAuthStartRequest,\n} from '../../lib/oauth';\nimport {\n executeFetchUserProfileStrategy,\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n AuthProviderFactory,\n AuthHandler,\n RedirectInfo,\n SignInResolver,\n} from '../types';\nimport { Logger } from 'winston';\nimport got from 'got';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\ntype Options = OAuthProviderOptions & {\n signInResolver?: SignInResolver<OAuthResult>;\n authHandler: AuthHandler<OAuthResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n logger: Logger;\n authorizationUrl?: string;\n tokenUrl?: string;\n};\n\nexport class MicrosoftAuthProvider implements OAuthHandlers {\n private readonly _strategy: MicrosoftStrategy;\n private readonly signInResolver?: SignInResolver<OAuthResult>;\n private readonly authHandler: AuthHandler<OAuthResult>;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n\n constructor(options: Options) {\n this.signInResolver = options.signInResolver;\n this.authHandler = options.authHandler;\n this.tokenIssuer = options.tokenIssuer;\n this.logger = options.logger;\n this.catalogIdentityClient = options.catalogIdentityClient;\n\n this._strategy = new MicrosoftStrategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n authorizationURL: options.authorizationUrl,\n tokenURL: options.tokenUrl,\n passReqToCallback: false as true,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(undefined, { fullProfile, accessToken, params }, { refreshToken });\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n scope: req.scope,\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n return {\n response: await this.handleResult(result),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const { accessToken, params } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: req.refreshToken,\n });\n }\n\n private async handleResult(result: OAuthResult) {\n const photo = await this.getUserPhoto(result.accessToken);\n result.fullProfile.photos = photo ? [{ value: photo }] : undefined;\n\n const { profile } = await this.authHandler(result);\n\n const response: OAuthResponse = {\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n profile,\n };\n\n if (this.signInResolver) {\n response.backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n }\n\n return response;\n }\n\n private getUserPhoto(accessToken: string): Promise<string | undefined> {\n return new Promise(resolve => {\n got\n .get('https://graph.microsoft.com/v1.0/me/photos/48x48/$value', {\n encoding: 'binary',\n responseType: 'buffer',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n })\n .then(photoData => {\n const photoURL = `data:image/jpeg;base64,${Buffer.from(\n photoData.body,\n ).toString('base64')}`;\n resolve(photoURL);\n })\n .catch(error => {\n this.logger.warn(\n `Could not retrieve user profile photo from Microsoft Graph API: ${error}`,\n );\n // User profile photo is optional, ignore errors and resolve undefined\n resolve(undefined);\n });\n });\n }\n}\n\nexport const microsoftEmailSignInResolver: SignInResolver<OAuthResult> = async (\n info,\n ctx,\n) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error('Microsoft profile contained no email');\n }\n\n const entity = await ctx.catalogIdentityClient.findUser({\n annotations: {\n 'microsoft.com/email': profile.email,\n },\n });\n\n const claims = getEntityClaims(entity);\n const token = await ctx.tokenIssuer.issueToken({ claims });\n\n return { id: entity.metadata.name, entity, token };\n};\n\nexport const microsoftDefaultSignInResolver: SignInResolver<OAuthResult> =\n async (info, ctx) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error('Profile contained no email');\n }\n\n const userId = profile.email.split('@')[0];\n\n const token = await ctx.tokenIssuer.issueToken({\n claims: { sub: userId, ent: [`user:default/${userId}`] },\n });\n\n return { id: userId, token };\n };\n\nexport type MicrosoftProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<OAuthResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n signIn?: {\n /**\n * Maps an auth result to a Backstage identity for the user.\n */\n resolver?: SignInResolver<OAuthResult>;\n };\n};\n\nexport const createMicrosoftProvider = (\n options?: MicrosoftProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const tenantId = envConfig.getString('tenantId');\n\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n const authorizationUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`;\n const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<OAuthResult> = options?.authHandler\n ? options.authHandler\n : async ({ fullProfile, params }) => ({\n profile: makeProfileInfo(fullProfile, params.id_token),\n });\n\n const signInResolverFn =\n options?.signIn?.resolver ?? microsoftDefaultSignInResolver;\n\n const signInResolver: SignInResolver<OAuthResult> = info =>\n signInResolverFn(info, {\n catalogIdentityClient,\n tokenIssuer,\n logger,\n });\n\n const provider = new MicrosoftAuthProvider({\n clientId,\n clientSecret,\n callbackUrl,\n authorizationUrl,\n tokenUrl,\n authHandler,\n signInResolver,\n catalogIdentityClient,\n logger,\n tokenIssuer,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: false,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport passport from 'passport';\nimport { Strategy as OAuth2Strategy } from 'passport-oauth2';\nimport {\n encodeState,\n OAuthAdapter,\n OAuthEnvironmentHandler,\n OAuthHandlers,\n OAuthProviderOptions,\n OAuthRefreshRequest,\n OAuthResponse,\n OAuthResult,\n OAuthStartRequest,\n} from '../../lib/oauth';\nimport {\n executeFetchUserProfileStrategy,\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n AuthHandler,\n AuthProviderFactory,\n RedirectInfo,\n SignInResolver,\n} from '../types';\nimport { CatalogIdentityClient } from '../../lib/catalog';\nimport { TokenIssuer } from '../../identity';\nimport { Logger } from 'winston';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\nexport type OAuth2AuthProviderOptions = OAuthProviderOptions & {\n signInResolver?: SignInResolver<OAuthResult>;\n authHandler: AuthHandler<OAuthResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n authorizationUrl: string;\n tokenUrl: string;\n scope?: string;\n logger: Logger;\n};\n\nexport class OAuth2AuthProvider implements OAuthHandlers {\n private readonly _strategy: OAuth2Strategy;\n private readonly signInResolver?: SignInResolver<OAuthResult>;\n private readonly authHandler: AuthHandler<OAuthResult>;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n\n constructor(options: OAuth2AuthProviderOptions) {\n this.signInResolver = options.signInResolver;\n this.authHandler = options.authHandler;\n this.tokenIssuer = options.tokenIssuer;\n this.catalogIdentityClient = options.catalogIdentityClient;\n this.logger = options.logger;\n\n this._strategy = new OAuth2Strategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n authorizationURL: options.authorizationUrl,\n tokenURL: options.tokenUrl,\n passReqToCallback: false as true,\n scope: options.scope,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(\n undefined,\n {\n fullProfile,\n accessToken,\n refreshToken,\n params,\n },\n {\n refreshToken,\n },\n );\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n accessType: 'offline',\n prompt: 'consent',\n scope: req.scope,\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n return {\n response: await this.handleResult(result),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const refreshTokenResponse = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n const {\n accessToken,\n params,\n refreshToken: updatedRefreshToken,\n } = refreshTokenResponse;\n\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: updatedRefreshToken,\n });\n }\n\n private async handleResult(result: OAuthResult) {\n const { profile } = await this.authHandler(result);\n\n const response: OAuthResponse = {\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n refreshToken: result.refreshToken,\n },\n profile,\n };\n\n if (this.signInResolver) {\n response.backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n }\n\n return response;\n }\n}\n\nexport const oAuth2DefaultSignInResolver: SignInResolver<OAuthResult> = async (\n info,\n ctx,\n) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error('Profile contained no email');\n }\n\n const userId = profile.email.split('@')[0];\n\n const token = await ctx.tokenIssuer.issueToken({\n claims: { sub: userId, ent: [`user:default/${userId}`] },\n });\n\n return { id: userId, token };\n};\n\nexport type OAuth2ProviderOptions = {\n authHandler?: AuthHandler<OAuthResult>;\n\n signIn?: {\n resolver?: SignInResolver<OAuthResult>;\n };\n};\n\nexport const createOAuth2Provider = (\n options?: OAuth2ProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n const authorizationUrl = envConfig.getString('authorizationUrl');\n const tokenUrl = envConfig.getString('tokenUrl');\n const scope = envConfig.getOptionalString('scope');\n const disableRefresh =\n envConfig.getOptionalBoolean('disableRefresh') ?? false;\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<OAuthResult> = options?.authHandler\n ? options.authHandler\n : async ({ fullProfile, params }) => ({\n profile: makeProfileInfo(fullProfile, params.id_token),\n });\n\n const signInResolverFn =\n options?.signIn?.resolver ?? oAuth2DefaultSignInResolver;\n\n const signInResolver: SignInResolver<OAuthResult> = info =>\n signInResolverFn(info, {\n catalogIdentityClient,\n tokenIssuer,\n logger,\n });\n\n const provider = new OAuth2AuthProvider({\n clientId,\n clientSecret,\n tokenIssuer,\n catalogIdentityClient,\n callbackUrl,\n signInResolver,\n authHandler,\n authorizationUrl,\n tokenUrl,\n scope,\n logger,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 express from 'express';\nimport {\n OAuthAdapter,\n OAuthProviderOptions,\n OAuthHandlers,\n OAuthResponse,\n OAuthEnvironmentHandler,\n OAuthStartRequest,\n encodeState,\n OAuthRefreshRequest,\n OAuthResult,\n} from '../../lib/oauth';\nimport { Strategy as OktaStrategy } from 'passport-okta-oauth';\nimport passport from 'passport';\nimport {\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n executeFetchUserProfileStrategy,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n AuthProviderFactory,\n AuthHandler,\n RedirectInfo,\n SignInResolver,\n} from '../types';\nimport { StateStore } from 'passport-oauth2';\nimport { CatalogIdentityClient, getEntityClaims } from '../../lib/catalog';\nimport { TokenIssuer } from '../../identity';\nimport { Logger } from 'winston';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\nexport type OktaAuthProviderOptions = OAuthProviderOptions & {\n audience: string;\n signInResolver?: SignInResolver<OAuthResult>;\n authHandler: AuthHandler<OAuthResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n logger: Logger;\n};\n\nexport class OktaAuthProvider implements OAuthHandlers {\n private readonly _strategy: any;\n private readonly _signInResolver?: SignInResolver<OAuthResult>;\n private readonly _authHandler: AuthHandler<OAuthResult>;\n private readonly _tokenIssuer: TokenIssuer;\n private readonly _catalogIdentityClient: CatalogIdentityClient;\n private readonly _logger: Logger;\n\n /**\n * Due to passport-okta-oauth forcing options.state = true,\n * passport-oauth2 requires express-session to be installed\n * so that the 'state' parameter of the oauth2 flow can be stored.\n * This implementation of StateStore matches the NullStore found within\n * passport-oauth2, which is the StateStore implementation used when options.state = false,\n * allowing us to avoid using express-session in order to integrate with Okta.\n */\n private _store: StateStore = {\n store(_req: express.Request, cb: any) {\n cb(null, null);\n },\n verify(_req: express.Request, _state: string, cb: any) {\n cb(null, true);\n },\n };\n\n constructor(options: OktaAuthProviderOptions) {\n this._signInResolver = options.signInResolver;\n this._authHandler = options.authHandler;\n this._tokenIssuer = options.tokenIssuer;\n this._catalogIdentityClient = options.catalogIdentityClient;\n this._logger = options.logger;\n\n this._strategy = new OktaStrategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n audience: options.audience,\n passReqToCallback: false as true,\n store: this._store,\n response_type: 'code',\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(\n undefined,\n {\n accessToken,\n refreshToken,\n params,\n fullProfile,\n },\n {\n refreshToken,\n },\n );\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n accessType: 'offline',\n prompt: 'consent',\n scope: req.scope,\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n return {\n response: await this.handleResult(result),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const { accessToken, params } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: req.refreshToken,\n });\n }\n\n private async handleResult(result: OAuthResult) {\n const { profile } = await this._authHandler(result);\n\n const response: OAuthResponse = {\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n profile,\n };\n\n if (this._signInResolver) {\n response.backstageIdentity = await this._signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this._tokenIssuer,\n catalogIdentityClient: this._catalogIdentityClient,\n logger: this._logger,\n },\n );\n }\n\n return response;\n }\n}\n\nexport const oktaEmailSignInResolver: SignInResolver<OAuthResult> = async (\n info,\n ctx,\n) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error('Okta profile contained no email');\n }\n\n const entity = await ctx.catalogIdentityClient.findUser({\n annotations: {\n 'okta.com/email': profile.email,\n },\n });\n\n const claims = getEntityClaims(entity);\n const token = await ctx.tokenIssuer.issueToken({ claims });\n\n return { id: entity.metadata.name, entity, token };\n};\n\nexport const oktaDefaultSignInResolver: SignInResolver<OAuthResult> = async (\n info,\n ctx,\n) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error('Okta profile contained no email');\n }\n\n // TODO(Rugvip): Hardcoded to the local part of the email for now\n const userId = profile.email.split('@')[0];\n\n const token = await ctx.tokenIssuer.issueToken({\n claims: { sub: userId, ent: [`user:default/${userId}`] },\n });\n\n return { id: userId, token };\n};\n\nexport type OktaProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<OAuthResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n signIn?: {\n /**\n * Maps an auth result to a Backstage identity for the user.\n */\n resolver?: SignInResolver<OAuthResult>;\n };\n};\n\nexport const createOktaProvider = (\n _options?: OktaProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const audience = envConfig.getString('audience');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n // This is a safe assumption as `passport-okta-oauth` uses the audience\n // as the base for building the authorization, token, and user info URLs.\n // https://github.com/fischerdan/passport-okta-oauth/blob/ea9ac42d/lib/passport-okta-oauth/oauth2.js#L12-L14\n if (!audience.startsWith('https://')) {\n throw new Error(\"URL for 'audience' must start with 'https://'.\");\n }\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<OAuthResult> = _options?.authHandler\n ? _options.authHandler\n : async ({ fullProfile, params }) => ({\n profile: makeProfileInfo(fullProfile, params.id_token),\n });\n\n const signInResolverFn =\n _options?.signIn?.resolver ?? oktaDefaultSignInResolver;\n\n const signInResolver: SignInResolver<OAuthResult> = info =>\n signInResolverFn(info, {\n catalogIdentityClient,\n tokenIssuer,\n logger,\n });\n\n const provider = new OktaAuthProvider({\n audience,\n clientId,\n clientSecret,\n callbackUrl,\n authHandler,\n signInResolver,\n tokenIssuer,\n catalogIdentityClient,\n logger,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: false,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport passport, { Profile as PassportProfile } from 'passport';\nimport { Strategy as BitbucketStrategy } from 'passport-bitbucket-oauth2';\nimport { TokenIssuer } from '../../identity/types';\nimport { CatalogIdentityClient, getEntityClaims } from '../../lib/catalog';\nimport {\n encodeState,\n OAuthAdapter,\n OAuthEnvironmentHandler,\n OAuthHandlers,\n OAuthProviderOptions,\n OAuthRefreshRequest,\n OAuthResponse,\n OAuthResult,\n OAuthStartRequest,\n} from '../../lib/oauth';\nimport {\n executeFetchUserProfileStrategy,\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n AuthProviderFactory,\n AuthHandler,\n RedirectInfo,\n SignInResolver,\n} from '../types';\nimport { Logger } from 'winston';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\ntype Options = OAuthProviderOptions & {\n signInResolver?: SignInResolver<OAuthResult>;\n authHandler: AuthHandler<BitbucketOAuthResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n logger: Logger;\n};\n\nexport type BitbucketOAuthResult = {\n fullProfile: BitbucketPassportProfile;\n params: {\n id_token?: string;\n scope: string;\n expires_in: number;\n };\n accessToken: string;\n refreshToken?: string;\n};\n\nexport type BitbucketPassportProfile = PassportProfile & {\n id?: string;\n displayName?: string;\n username?: string;\n avatarUrl?: string;\n _json?: {\n links?: {\n avatar?: {\n href?: string;\n };\n };\n };\n};\n\nexport class BitbucketAuthProvider implements OAuthHandlers {\n private readonly _strategy: BitbucketStrategy;\n private readonly signInResolver?: SignInResolver<OAuthResult>;\n private readonly authHandler: AuthHandler<OAuthResult>;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n\n constructor(options: Options) {\n this.signInResolver = options.signInResolver;\n this.authHandler = options.authHandler;\n this.tokenIssuer = options.tokenIssuer;\n this.catalogIdentityClient = options.catalogIdentityClient;\n this.logger = options.logger;\n this._strategy = new BitbucketStrategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n // We need passReqToCallback set to false to get params, but there's\n // no matching type signature for that, so instead behold this beauty\n passReqToCallback: false as true,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(\n undefined,\n {\n fullProfile,\n params,\n accessToken,\n refreshToken,\n },\n {\n refreshToken,\n },\n );\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n accessType: 'offline',\n prompt: 'consent',\n scope: req.scope,\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n return {\n response: await this.handleResult(result),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const { accessToken, params } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: req.refreshToken,\n });\n }\n\n private async handleResult(result: BitbucketOAuthResult) {\n result.fullProfile.avatarUrl =\n result.fullProfile._json!.links!.avatar!.href;\n const { profile } = await this.authHandler(result);\n\n const response: OAuthResponse = {\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n profile,\n };\n\n if (this.signInResolver) {\n response.backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n }\n\n return response;\n }\n}\n\nexport const bitbucketUsernameSignInResolver: SignInResolver<BitbucketOAuthResult> =\n async (info, ctx) => {\n const { result } = info;\n\n if (!result.fullProfile.username) {\n throw new Error('Bitbucket profile contained no Username');\n }\n\n const entity = await ctx.catalogIdentityClient.findUser({\n annotations: {\n 'bitbucket.org/username': result.fullProfile.username,\n },\n });\n\n const claims = getEntityClaims(entity);\n const token = await ctx.tokenIssuer.issueToken({ claims });\n\n return { id: entity.metadata.name, entity, token };\n };\n\nexport const bitbucketUserIdSignInResolver: SignInResolver<BitbucketOAuthResult> =\n async (info, ctx) => {\n const { result } = info;\n\n if (!result.fullProfile.id) {\n throw new Error('Bitbucket profile contained no User ID');\n }\n\n const entity = await ctx.catalogIdentityClient.findUser({\n annotations: {\n 'bitbucket.org/user-id': result.fullProfile.id,\n },\n });\n\n const claims = getEntityClaims(entity);\n const token = await ctx.tokenIssuer.issueToken({ claims });\n\n return { id: entity.metadata.name, entity, token };\n };\n\nexport type BitbucketProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<OAuthResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n signIn?: {\n /**\n * Maps an auth result to a Backstage identity for the user.\n */\n resolver: SignInResolver<OAuthResult>;\n };\n};\n\nexport const createBitbucketProvider = (\n options?: BitbucketProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<BitbucketOAuthResult> =\n options?.authHandler\n ? options.authHandler\n : async ({ fullProfile, params }) => ({\n profile: makeProfileInfo(fullProfile, params.id_token),\n });\n\n const provider = new BitbucketAuthProvider({\n clientId,\n clientSecret,\n callbackUrl,\n signInResolver: options?.signIn?.resolver,\n authHandler,\n tokenIssuer,\n catalogIdentityClient,\n logger,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: false,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport OAuth2Strategy, { InternalOAuthError } from 'passport-oauth2';\nimport { Profile } from 'passport';\n\ninterface ProfileResponse {\n account_id: string;\n email: string;\n name: string;\n picture: string;\n nickname: string;\n}\n\ninterface AtlassianStrategyOptions {\n clientID: string;\n clientSecret: string;\n callbackURL: string;\n scope: string;\n}\n\nconst defaultScopes = ['offline_access', 'read:me'];\n\nexport default class AtlassianStrategy extends OAuth2Strategy {\n private readonly profileURL: string;\n\n constructor(\n options: AtlassianStrategyOptions,\n verify: OAuth2Strategy.VerifyFunction,\n ) {\n if (!options.scope) {\n throw new TypeError('Atlassian requires a scope option');\n }\n\n const scopes = options.scope.split(' ');\n\n const optionsWithURLs = {\n ...options,\n authorizationURL: `https://auth.atlassian.com/authorize`,\n tokenURL: `https://auth.atlassian.com/oauth/token`,\n scope: Array.from(new Set([...defaultScopes, ...scopes])),\n };\n\n super(optionsWithURLs, verify);\n this.profileURL = 'https://api.atlassian.com/me';\n this.name = 'atlassian';\n\n this._oauth2.useAuthorizationHeaderforGET(true);\n }\n\n authorizationParams() {\n return {\n audience: 'api.atlassian.com',\n prompt: 'consent',\n };\n }\n\n userProfile(\n accessToken: string,\n done: (err?: Error | null, profile?: any) => void,\n ): void {\n this._oauth2.get(this.profileURL, accessToken, (err, body) => {\n if (err) {\n return done(\n new InternalOAuthError(\n 'Failed to fetch user profile',\n err.statusCode,\n ),\n );\n }\n\n if (!body) {\n return done(\n new Error('Failed to fetch user profile, body cannot be empty'),\n );\n }\n\n try {\n const json = typeof body !== 'string' ? body.toString() : body;\n const profile = AtlassianStrategy.parse(json);\n return done(null, profile);\n } catch (e) {\n return done(new Error('Failed to parse user profile'));\n }\n });\n }\n\n static parse(json: string): Profile {\n const resp = JSON.parse(json) as ProfileResponse;\n\n return {\n id: resp.account_id,\n provider: 'atlassian',\n username: resp.nickname,\n displayName: resp.name,\n emails: [{ value: resp.email }],\n photos: [{ value: resp.picture }],\n };\n }\n}\n","/*\n * Copyright 2020 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 */\n\nimport AtlassianStrategy from './strategy';\nimport {\n encodeState,\n OAuthAdapter,\n OAuthEnvironmentHandler,\n OAuthHandlers,\n OAuthProviderOptions,\n OAuthRefreshRequest,\n OAuthResponse,\n OAuthResult,\n OAuthStartRequest,\n} from '../../lib/oauth';\nimport passport from 'passport';\nimport {\n executeFetchUserProfileStrategy,\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport {\n AuthHandler,\n AuthProviderFactory,\n RedirectInfo,\n SignInResolver,\n} from '../types';\nimport express from 'express';\nimport { TokenIssuer } from '../../identity';\nimport { CatalogIdentityClient } from '../../lib/catalog';\nimport { Logger } from 'winston';\n\nexport type AtlassianAuthProviderOptions = OAuthProviderOptions & {\n scopes: string;\n signInResolver?: SignInResolver<OAuthResult>;\n authHandler: AuthHandler<OAuthResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n logger: Logger;\n};\n\nexport const atlassianDefaultAuthHandler: AuthHandler<OAuthResult> = async ({\n fullProfile,\n params,\n}) => ({\n profile: makeProfileInfo(fullProfile, params.id_token),\n});\n\nexport class AtlassianAuthProvider implements OAuthHandlers {\n private readonly _strategy: AtlassianStrategy;\n private readonly signInResolver?: SignInResolver<OAuthResult>;\n private readonly authHandler: AuthHandler<OAuthResult>;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n\n constructor(options: AtlassianAuthProviderOptions) {\n this.catalogIdentityClient = options.catalogIdentityClient;\n this.logger = options.logger;\n this.tokenIssuer = options.tokenIssuer;\n this.authHandler = options.authHandler;\n this.signInResolver = options.signInResolver;\n\n this._strategy = new AtlassianStrategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n scope: options.scopes,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult>,\n ) => {\n done(undefined, {\n fullProfile,\n accessToken,\n refreshToken,\n params,\n });\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result } = await executeFrameHandlerStrategy<OAuthResult>(\n req,\n this._strategy,\n );\n\n return {\n response: await this.handleResult(result),\n refreshToken: result.refreshToken ?? '',\n };\n }\n\n private async handleResult(result: OAuthResult): Promise<OAuthResponse> {\n const { profile } = await this.authHandler(result);\n\n const response: OAuthResponse = {\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n profile,\n };\n\n if (this.signInResolver) {\n response.backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n }\n\n return response;\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const {\n accessToken,\n params,\n refreshToken: newRefreshToken,\n } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n\n return this.handleResult({\n fullProfile,\n params,\n accessToken,\n refreshToken: newRefreshToken,\n });\n }\n}\n\nexport type AtlassianProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<OAuthResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n signIn?: {\n resolver: SignInResolver<OAuthResult>;\n };\n};\n\nexport const createAtlassianProvider = (\n options?: AtlassianProviderOptions,\n): AuthProviderFactory => {\n return ({\n providerId,\n globalConfig,\n config,\n tokenIssuer,\n catalogApi,\n logger,\n }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const scopes = envConfig.getString('scopes');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<OAuthResult> =\n options?.authHandler ?? atlassianDefaultAuthHandler;\n\n const provider = new AtlassianAuthProvider({\n clientId,\n clientSecret,\n scopes,\n callbackUrl,\n authHandler,\n signInResolver: options?.signIn?.resolver,\n catalogIdentityClient,\n logger,\n tokenIssuer,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: true,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2021 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 AuthHandler,\n AuthProviderFactory,\n AuthProviderRouteHandlers,\n AuthResponse,\n SignInResolver,\n} from '../types';\nimport express from 'express';\nimport fetch from 'cross-fetch';\nimport * as crypto from 'crypto';\nimport { KeyObject } from 'crypto';\nimport { Logger } from 'winston';\nimport NodeCache from 'node-cache';\nimport { JWT } from 'jose';\nimport { TokenIssuer } from '../../identity/types';\nimport { CatalogIdentityClient } from '../../lib/catalog';\nimport { Profile as PassportProfile } from 'passport';\nimport { makeProfileInfo } from '../../lib/passport';\nimport { AuthenticationError } from '@backstage/errors';\n\nexport const ALB_JWT_HEADER = 'x-amzn-oidc-data';\nexport const ALB_ACCESSTOKEN_HEADER = 'x-amzn-oidc-accesstoken';\n\ntype Options = {\n region: string;\n issuer?: string;\n logger: Logger;\n authHandler: AuthHandler<AwsAlbResult>;\n signInResolver: SignInResolver<AwsAlbResult>;\n tokenIssuer: TokenIssuer;\n catalogIdentityClient: CatalogIdentityClient;\n};\n\nexport const getJWTHeaders = (input: string): AwsAlbHeaders => {\n const encoded = input.split('.')[0];\n return JSON.parse(Buffer.from(encoded, 'base64').toString('utf8'));\n};\n\nexport type AwsAlbHeaders = {\n alg: string;\n kid: string;\n signer: string;\n iss: string;\n client: string;\n exp: number;\n};\n\nexport type AwsAlbClaims = {\n sub: string;\n name: string;\n family_name: string;\n given_name: string;\n picture: string;\n email: string;\n exp: number;\n iss: string;\n};\n\nexport type AwsAlbResult = {\n fullProfile: PassportProfile;\n expiresInSeconds?: number;\n accessToken: string;\n};\n\nexport type AwsAlbProviderInfo = {\n /**\n * An access token issued for the signed in user.\n */\n accessToken: string;\n /**\n * Expiry of the access token in seconds.\n */\n expiresInSeconds?: number;\n};\n\nexport type AwsAlbResponse = AuthResponse<AwsAlbProviderInfo>;\n\nexport class AwsAlbAuthProvider implements AuthProviderRouteHandlers {\n private readonly region: string;\n private readonly issuer?: string;\n private readonly tokenIssuer: TokenIssuer;\n private readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly logger: Logger;\n private readonly keyCache: NodeCache;\n private readonly authHandler: AuthHandler<AwsAlbResult>;\n private readonly signInResolver: SignInResolver<AwsAlbResult>;\n\n constructor(options: Options) {\n this.region = options.region;\n this.issuer = options.issuer;\n this.authHandler = options.authHandler;\n this.signInResolver = options.signInResolver;\n this.tokenIssuer = options.tokenIssuer;\n this.catalogIdentityClient = options.catalogIdentityClient;\n this.logger = options.logger;\n this.keyCache = new NodeCache({ stdTTL: 3600 });\n }\n\n frameHandler(): Promise<void> {\n return Promise.resolve(undefined);\n }\n\n async refresh(req: express.Request, res: express.Response): Promise<void> {\n try {\n const result = await this.getResult(req);\n const response = await this.handleResult(result);\n res.json(response);\n } catch (e) {\n this.logger.error('Exception occurred during AWS ALB token refresh', e);\n res.status(401);\n res.end();\n }\n }\n\n start(): Promise<void> {\n return Promise.resolve(undefined);\n }\n\n private async getResult(req: express.Request): Promise<AwsAlbResult> {\n const jwt = req.header(ALB_JWT_HEADER);\n const accessToken = req.header(ALB_ACCESSTOKEN_HEADER);\n\n if (jwt === undefined) {\n throw new AuthenticationError(\n `Missing ALB OIDC header: ${ALB_JWT_HEADER}`,\n );\n }\n\n if (accessToken === undefined) {\n throw new AuthenticationError(\n `Missing ALB OIDC header: ${ALB_ACCESSTOKEN_HEADER}`,\n );\n }\n\n try {\n const headers = getJWTHeaders(jwt);\n const key = await this.getKey(headers.kid);\n const claims = JWT.verify(jwt, key) as AwsAlbClaims;\n\n if (this.issuer && claims.iss !== this.issuer) {\n throw new AuthenticationError('Issuer mismatch on JWT token');\n }\n\n const fullProfile: PassportProfile = {\n provider: 'unknown',\n id: claims.sub,\n displayName: claims.name,\n username: claims.email.split('@')[0].toLowerCase(),\n name: {\n familyName: claims.family_name,\n givenName: claims.given_name,\n },\n emails: [{ value: claims.email.toLowerCase() }],\n photos: [{ value: claims.picture }],\n };\n\n return {\n fullProfile,\n expiresInSeconds: claims.exp,\n accessToken,\n };\n } catch (e) {\n throw new Error(`Exception occurred during JWT processing: ${e}`);\n }\n }\n\n private async handleResult(result: AwsAlbResult): Promise<AwsAlbResponse> {\n const { profile } = await this.authHandler(result);\n const backstageIdentity = await this.signInResolver(\n {\n result,\n profile,\n },\n {\n tokenIssuer: this.tokenIssuer,\n catalogIdentityClient: this.catalogIdentityClient,\n logger: this.logger,\n },\n );\n\n return {\n providerInfo: {\n accessToken: result.accessToken,\n expiresInSeconds: result.expiresInSeconds,\n },\n backstageIdentity,\n profile,\n };\n }\n\n async getKey(keyId: string): Promise<KeyObject> {\n const optionalCacheKey = this.keyCache.get<KeyObject>(keyId);\n if (optionalCacheKey) {\n return crypto.createPublicKey(optionalCacheKey);\n }\n const keyText: string = await fetch(\n `https://public-keys.auth.elb.${this.region}.amazonaws.com/${keyId}`,\n ).then(response => response.text());\n const keyValue = crypto.createPublicKey(keyText);\n this.keyCache.set(keyId, keyValue.export({ format: 'pem', type: 'spki' }));\n return keyValue;\n }\n}\n\nexport type AwsAlbProviderOptions = {\n /**\n * The profile transformation function used to verify and convert the auth response\n * into the profile that will be presented to the user.\n */\n authHandler?: AuthHandler<AwsAlbResult>;\n\n /**\n * Configure sign-in for this provider, without it the provider can not be used to sign users in.\n */\n signIn: {\n /**\n * Maps an auth result to a Backstage identity for the user.\n */\n resolver: SignInResolver<AwsAlbResult>;\n };\n};\n\nexport const createAwsAlbProvider = (\n options?: AwsAlbProviderOptions,\n): AuthProviderFactory => {\n return ({ config, tokenIssuer, catalogApi, logger }) => {\n const region = config.getString('region');\n const issuer = config.getOptionalString('iss');\n\n if (options?.signIn.resolver === undefined) {\n throw new Error(\n 'SignInResolver is required to use this authentication provider',\n );\n }\n\n const catalogIdentityClient = new CatalogIdentityClient({\n catalogApi,\n tokenIssuer,\n });\n\n const authHandler: AuthHandler<AwsAlbResult> = options?.authHandler\n ? options.authHandler\n : async ({ fullProfile }) => ({\n profile: makeProfileInfo(fullProfile),\n });\n\n const signInResolver = options?.signIn.resolver;\n\n return new AwsAlbAuthProvider({\n region,\n issuer,\n signInResolver,\n authHandler,\n tokenIssuer,\n catalogIdentityClient,\n logger,\n });\n };\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport {\n Issuer,\n Client,\n Strategy as OidcStrategy,\n TokenSet,\n UserinfoResponse,\n} from 'openid-client';\nimport {\n OAuthAdapter,\n OAuthProviderOptions,\n OAuthHandlers,\n OAuthResponse,\n OAuthEnvironmentHandler,\n OAuthStartRequest,\n encodeState,\n OAuthRefreshRequest,\n} from '../../lib/oauth';\nimport {\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport { RedirectInfo, AuthProviderFactory } from '../types';\n\ntype PrivateInfo = {\n refreshToken?: string;\n};\n\ntype OidcImpl = {\n strategy: OidcStrategy<UserinfoResponse, Client>;\n client: Client;\n};\n\ntype AuthResult = {\n tokenset: TokenSet;\n userinfo: UserinfoResponse;\n};\n\nexport type Options = OAuthProviderOptions & {\n metadataUrl: string;\n scope?: string;\n prompt?: string;\n tokenSignedResponseAlg?: string;\n};\n\nexport class OidcAuthProvider implements OAuthHandlers {\n private readonly implementation: Promise<OidcImpl>;\n private readonly scope?: string;\n private readonly prompt?: string;\n\n constructor(options: Options) {\n this.implementation = this.setupStrategy(options);\n this.scope = options.scope;\n this.prompt = options.prompt;\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n const { strategy } = await this.implementation;\n const options: Record<string, string> = {\n accessType: 'offline',\n scope: req.scope || this.scope || 'openid profile email',\n state: encodeState(req.state),\n };\n const prompt = this.prompt || 'none';\n if (prompt !== 'auto') {\n options.prompt = prompt;\n }\n return await executeRedirectStrategy(req, strategy, options);\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken?: string }> {\n const { strategy } = await this.implementation;\n const strategyResponse = await executeFrameHandlerStrategy<\n AuthResult,\n PrivateInfo\n >(req, strategy);\n const {\n result: { userinfo, tokenset },\n privateInfo,\n } = strategyResponse;\n const identityResponse = await this.populateIdentity({\n profile: {\n displayName: userinfo.name,\n email: userinfo.email,\n picture: userinfo.picture,\n },\n providerInfo: {\n idToken: tokenset.id_token,\n accessToken: tokenset.access_token || '',\n scope: tokenset.scope || '',\n expiresInSeconds: tokenset.expires_in,\n },\n });\n return {\n response: identityResponse,\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const { client } = await this.implementation;\n const tokenset = await client.refresh(req.refreshToken);\n if (!tokenset.access_token) {\n throw new Error('Refresh failed');\n }\n const profile = await client.userinfo(tokenset.access_token);\n\n return this.populateIdentity({\n providerInfo: {\n accessToken: tokenset.access_token,\n refreshToken: tokenset.refresh_token,\n expiresInSeconds: tokenset.expires_in,\n idToken: tokenset.id_token,\n scope: tokenset.scope || '',\n },\n profile,\n });\n }\n\n private async setupStrategy(options: Options): Promise<OidcImpl> {\n const issuer = await Issuer.discover(options.metadataUrl);\n const client = new issuer.Client({\n client_id: options.clientId,\n client_secret: options.clientSecret,\n redirect_uris: [options.callbackUrl],\n response_types: ['code'],\n id_token_signed_response_alg: options.tokenSignedResponseAlg || 'RS256',\n scope: options.scope || '',\n });\n\n const strategy = new OidcStrategy(\n {\n client,\n passReqToCallback: false as true,\n },\n (\n tokenset: TokenSet,\n userinfo: UserinfoResponse,\n done: PassportDoneCallback<AuthResult, PrivateInfo>,\n ) => {\n if (typeof done !== 'function') {\n throw new Error(\n 'OIDC IdP must provide a userinfo_endpoint in the metadata response',\n );\n }\n done(\n undefined,\n { tokenset, userinfo },\n {\n refreshToken: tokenset.refresh_token,\n },\n );\n },\n );\n strategy.error = console.error;\n return { strategy, client };\n }\n\n // Use this function to grab the user profile info from the token\n // Then populate the profile with it\n private async populateIdentity(\n response: OAuthResponse,\n ): Promise<OAuthResponse> {\n const { profile } = response;\n\n if (!profile.email) {\n throw new Error('Profile does not contain an email');\n }\n const id = profile.email.split('@')[0];\n\n return { ...response, backstageIdentity: { id } };\n }\n}\n\nexport type OidcProviderOptions = {};\n\nexport const createOidcProvider = (\n _options?: OidcProviderOptions,\n): AuthProviderFactory => {\n return ({ providerId, globalConfig, config, tokenIssuer }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n const metadataUrl = envConfig.getString('metadataUrl');\n const tokenSignedResponseAlg = envConfig.getOptionalString(\n 'tokenSignedResponseAlg',\n );\n const scope = envConfig.getOptionalString('scope');\n const prompt = envConfig.getOptionalString('prompt');\n\n const provider = new OidcAuthProvider({\n clientId,\n clientSecret,\n callbackUrl,\n tokenSignedResponseAlg,\n metadataUrl,\n scope,\n prompt,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: false,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport { SamlConfig } from 'passport-saml/lib/passport-saml/types';\nimport {\n Strategy as SamlStrategy,\n Profile as SamlProfile,\n VerifyWithoutRequest,\n} from 'passport-saml';\nimport {\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport { AuthProviderRouteHandlers, AuthProviderFactory } from '../types';\nimport { postMessageResponse } from '../../lib/flow';\nimport { TokenIssuer } from '../../identity/types';\nimport { isError } from '@backstage/errors';\n\ntype SamlInfo = {\n fullProfile: any;\n};\n\ntype Options = SamlConfig & {\n tokenIssuer: TokenIssuer;\n appUrl: string;\n};\n\nexport class SamlAuthProvider implements AuthProviderRouteHandlers {\n private readonly strategy: SamlStrategy;\n private readonly tokenIssuer: TokenIssuer;\n private readonly appUrl: string;\n\n constructor(options: Options) {\n this.appUrl = options.appUrl;\n this.tokenIssuer = options.tokenIssuer;\n this.strategy = new SamlStrategy({ ...options }, ((\n fullProfile: SamlProfile,\n done: PassportDoneCallback<SamlInfo>,\n ) => {\n // TODO: There's plenty more validation and profile handling to do here,\n // this provider is currently only intended to validate the provider pattern\n // for non-oauth auth flows.\n // TODO: This flow doesn't issue an identity token that can be used to validate\n // the identity of the user in other backends, which we need in some form.\n done(undefined, { fullProfile });\n }) as VerifyWithoutRequest);\n }\n\n async start(req: express.Request, res: express.Response): Promise<void> {\n const { url } = await executeRedirectStrategy(req, this.strategy, {});\n res.redirect(url);\n }\n\n async frameHandler(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n try {\n const { result } = await executeFrameHandlerStrategy<SamlInfo>(\n req,\n this.strategy,\n );\n\n const id = result.fullProfile.nameID;\n\n const idToken = await this.tokenIssuer.issueToken({\n claims: { sub: id },\n });\n\n return postMessageResponse(res, this.appUrl, {\n type: 'authorization_response',\n response: {\n profile: {\n email: result.fullProfile.email,\n displayName: result.fullProfile.displayName,\n },\n providerInfo: {},\n backstageIdentity: { id, idToken },\n },\n });\n } catch (error) {\n const { name, message } = isError(error)\n ? error\n : new Error('Encountered invalid error'); // Being a bit safe and not forwarding the bad value\n return postMessageResponse(res, this.appUrl, {\n type: 'authorization_response',\n error: { name, message },\n });\n }\n }\n\n async logout(_req: express.Request, res: express.Response): Promise<void> {\n res.send('noop');\n }\n\n identifyEnv(): string | undefined {\n return undefined;\n }\n}\n\ntype SignatureAlgorithm = 'sha1' | 'sha256' | 'sha512';\n\nexport type SamlProviderOptions = {};\n\nexport const createSamlProvider = (\n _options?: SamlProviderOptions,\n): AuthProviderFactory => {\n return ({ providerId, globalConfig, config, tokenIssuer }) => {\n const opts = {\n callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,\n entryPoint: config.getString('entryPoint'),\n logoutUrl: config.getOptionalString('logoutUrl'),\n audience: config.getOptionalString('audience'),\n issuer: config.getString('issuer'),\n cert: config.getString('cert'),\n privateCert: config.getOptionalString('privateKey'),\n authnContext: config.getOptionalStringArray('authnContext'),\n identifierFormat: config.getOptionalString('identifierFormat'),\n decryptionPvk: config.getOptionalString('decryptionPvk'),\n signatureAlgorithm: config.getOptionalString('signatureAlgorithm') as\n | SignatureAlgorithm\n | undefined,\n digestAlgorithm: config.getOptionalString('digestAlgorithm'),\n acceptedClockSkewMs: config.getOptionalNumber('acceptedClockSkewMs'),\n\n tokenIssuer,\n appUrl: globalConfig.appUrl,\n };\n\n return new SamlAuthProvider(opts);\n };\n};\n","/*\n * Copyright 2020 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 OAuth2Strategy from 'passport-oauth2';\n\nexport interface Auth0StrategyOptionsWithRequest {\n clientID: string;\n clientSecret: string;\n callbackURL: string;\n domain: string;\n passReqToCallback: true;\n}\n\nexport default class Auth0Strategy extends OAuth2Strategy {\n constructor(\n options: Auth0StrategyOptionsWithRequest,\n verify: OAuth2Strategy.VerifyFunctionWithRequest,\n ) {\n const optionsWithURLs = {\n ...options,\n authorizationURL: `https://${options.domain}/authorize`,\n tokenURL: `https://${options.domain}/oauth/token`,\n userInfoURL: `https://${options.domain}/userinfo`,\n apiUrl: `https://${options.domain}/api`,\n };\n super(optionsWithURLs, verify);\n }\n}\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport passport from 'passport';\nimport Auth0Strategy from './strategy';\nimport {\n OAuthAdapter,\n OAuthProviderOptions,\n OAuthHandlers,\n OAuthResponse,\n OAuthEnvironmentHandler,\n OAuthStartRequest,\n encodeState,\n OAuthRefreshRequest,\n OAuthResult,\n} from '../../lib/oauth';\nimport {\n executeFetchUserProfileStrategy,\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport { RedirectInfo, AuthProviderFactory } from '../types';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\nexport type Auth0AuthProviderOptions = OAuthProviderOptions & {\n domain: string;\n};\n\nexport class Auth0AuthProvider implements OAuthHandlers {\n private readonly _strategy: Auth0Strategy;\n\n constructor(options: Auth0AuthProviderOptions) {\n this._strategy = new Auth0Strategy(\n {\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n domain: options.domain,\n passReqToCallback: false as true,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(\n undefined,\n {\n fullProfile,\n accessToken,\n refreshToken,\n params,\n },\n {\n refreshToken,\n },\n );\n },\n );\n }\n\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n accessType: 'offline',\n prompt: 'consent',\n scope: req.scope,\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n const profile = makeProfileInfo(result.fullProfile, result.params.id_token);\n\n return {\n response: await this.populateIdentity({\n profile,\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n }),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const { accessToken, params } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n const profile = makeProfileInfo(fullProfile, params.id_token);\n\n return this.populateIdentity({\n providerInfo: {\n accessToken,\n idToken: params.id_token,\n expiresInSeconds: params.expires_in,\n scope: params.scope,\n },\n profile,\n });\n }\n\n // Use this function to grab the user profile info from the token\n // Then populate the profile with it\n private async populateIdentity(\n response: OAuthResponse,\n ): Promise<OAuthResponse> {\n const { profile } = response;\n\n if (!profile.email) {\n throw new Error('Profile does not contain an email');\n }\n\n const id = profile.email.split('@')[0];\n\n return { ...response, backstageIdentity: { id } };\n }\n}\n\nexport type Auth0ProviderOptions = {};\n\nexport const createAuth0Provider = (\n _options?: Auth0ProviderOptions,\n): AuthProviderFactory => {\n return ({ providerId, globalConfig, config, tokenIssuer }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const domain = envConfig.getString('domain');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n const provider = new Auth0AuthProvider({\n clientId,\n clientSecret,\n callbackUrl,\n domain,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: true,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport { Strategy as OneLoginStrategy } from 'passport-onelogin-oauth';\nimport express from 'express';\nimport {\n OAuthAdapter,\n OAuthProviderOptions,\n OAuthHandlers,\n OAuthResponse,\n OAuthEnvironmentHandler,\n OAuthStartRequest,\n encodeState,\n OAuthRefreshRequest,\n OAuthResult,\n} from '../../lib/oauth';\nimport passport from 'passport';\nimport {\n executeFrameHandlerStrategy,\n executeRedirectStrategy,\n executeRefreshTokenStrategy,\n makeProfileInfo,\n executeFetchUserProfileStrategy,\n PassportDoneCallback,\n} from '../../lib/passport';\nimport { RedirectInfo, AuthProviderFactory } from '../types';\n\ntype PrivateInfo = {\n refreshToken: string;\n};\n\nexport type Options = OAuthProviderOptions & {\n issuer: string;\n};\n\nexport class OneLoginProvider implements OAuthHandlers {\n private readonly _strategy: any;\n\n constructor(options: Options) {\n this._strategy = new OneLoginStrategy(\n {\n issuer: options.issuer,\n clientID: options.clientId,\n clientSecret: options.clientSecret,\n callbackURL: options.callbackUrl,\n passReqToCallback: false as true,\n },\n (\n accessToken: any,\n refreshToken: any,\n params: any,\n fullProfile: passport.Profile,\n done: PassportDoneCallback<OAuthResult, PrivateInfo>,\n ) => {\n done(\n undefined,\n {\n accessToken,\n refreshToken,\n params,\n fullProfile,\n },\n {\n refreshToken,\n },\n );\n },\n );\n }\n async start(req: OAuthStartRequest): Promise<RedirectInfo> {\n return await executeRedirectStrategy(req, this._strategy, {\n accessType: 'offline',\n prompt: 'consent',\n scope: 'openid',\n state: encodeState(req.state),\n });\n }\n\n async handler(\n req: express.Request,\n ): Promise<{ response: OAuthResponse; refreshToken: string }> {\n const { result, privateInfo } = await executeFrameHandlerStrategy<\n OAuthResult,\n PrivateInfo\n >(req, this._strategy);\n\n const profile = makeProfileInfo(result.fullProfile, result.params.id_token);\n\n return {\n response: await this.populateIdentity({\n profile,\n providerInfo: {\n idToken: result.params.id_token,\n accessToken: result.accessToken,\n scope: result.params.scope,\n expiresInSeconds: result.params.expires_in,\n },\n }),\n refreshToken: privateInfo.refreshToken,\n };\n }\n\n async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {\n const { accessToken, params } = await executeRefreshTokenStrategy(\n this._strategy,\n req.refreshToken,\n req.scope,\n );\n\n const fullProfile = await executeFetchUserProfileStrategy(\n this._strategy,\n accessToken,\n );\n const profile = makeProfileInfo(fullProfile, params.id_token);\n\n return this.populateIdentity({\n providerInfo: {\n accessToken,\n idToken: params.id_token,\n expiresInSeconds: params.expires_in,\n scope: params.scope,\n },\n profile,\n });\n }\n\n private async populateIdentity(\n response: OAuthResponse,\n ): Promise<OAuthResponse> {\n const { profile } = response;\n\n if (!profile.email) {\n throw new Error('OIDC profile contained no email');\n }\n\n const id = profile.email.split('@')[0];\n\n return { ...response, backstageIdentity: { id } };\n }\n}\n\nexport type OneLoginProviderOptions = {};\n\nexport const createOneLoginProvider = (\n _options?: OneLoginProviderOptions,\n): AuthProviderFactory => {\n return ({ providerId, globalConfig, config, tokenIssuer }) =>\n OAuthEnvironmentHandler.mapConfig(config, envConfig => {\n const clientId = envConfig.getString('clientId');\n const clientSecret = envConfig.getString('clientSecret');\n const issuer = envConfig.getString('issuer');\n const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;\n\n const provider = new OneLoginProvider({\n clientId,\n clientSecret,\n callbackUrl,\n issuer,\n });\n\n return OAuthAdapter.fromConfig(globalConfig, provider, {\n disableRefresh: false,\n providerId,\n tokenIssuer,\n });\n });\n};\n","/*\n * Copyright 2020 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 */\n\nimport { createGithubProvider } from './github';\nimport { createGitlabProvider } from './gitlab';\nimport { createGoogleProvider } from './google';\nimport { createOAuth2Provider } from './oauth2';\nimport { createOidcProvider } from './oidc';\nimport { createOktaProvider } from './okta';\nimport { createSamlProvider } from './saml';\nimport { createAuth0Provider } from './auth0';\nimport { createMicrosoftProvider } from './microsoft';\nimport { createOneLoginProvider } from './onelogin';\nimport { AuthProviderFactory } from './types';\nimport { createAwsAlbProvider } from './aws-alb';\nimport { createBitbucketProvider } from './bitbucket';\nimport { createAtlassianProvider } from './atlassian';\n\nexport const factories: { [providerId: string]: AuthProviderFactory } = {\n google: createGoogleProvider(),\n github: createGithubProvider(),\n gitlab: createGitlabProvider(),\n saml: createSamlProvider(),\n okta: createOktaProvider(),\n auth0: createAuth0Provider(),\n microsoft: createMicrosoftProvider(),\n oauth2: createOAuth2Provider(),\n oidc: createOidcProvider(),\n onelogin: createOneLoginProvider(),\n awsalb: createAwsAlbProvider(),\n bitbucket: createBitbucketProvider(),\n atlassian: createAtlassianProvider(),\n};\n","/*\n * Copyright 2020 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 */\n\nimport Router from 'express-promise-router';\nimport { TokenIssuer } from './types';\n\nexport type Options = {\n baseUrl: string;\n tokenIssuer: TokenIssuer;\n};\n\nexport function createOidcRouter(options: Options) {\n const { baseUrl, tokenIssuer } = options;\n\n const router = Router();\n\n const config = {\n issuer: baseUrl,\n token_endpoint: `${baseUrl}/v1/token`,\n userinfo_endpoint: `${baseUrl}/v1/userinfo`,\n jwks_uri: `${baseUrl}/.well-known/jwks.json`,\n response_types_supported: ['id_token'],\n subject_types_supported: ['public'],\n id_token_signing_alg_values_supported: ['RS256'],\n scopes_supported: ['openid'],\n token_endpoint_auth_methods_supported: [],\n claims_supported: ['sub'],\n grant_types_supported: [],\n };\n\n router.get('/.well-known/openid-configuration', (_req, res) => {\n res.json(config);\n });\n\n router.get('/.well-known/jwks.json', async (_req, res) => {\n const { keys } = await tokenIssuer.listPublicKeys();\n res.json({ keys });\n });\n\n router.get('/v1/token', (_req, res) => {\n res.status(501).send('Not Implemented');\n });\n\n router.get('/v1/userinfo', (_req, res) => {\n res.status(501).send('Not Implemented');\n });\n\n return router;\n}\n","/*\n * Copyright 2020 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 */\n\nimport fetch from 'cross-fetch';\nimport { JWK, JWT, JWKS, JSONWebKey } from 'jose';\nimport { BackstageIdentity } from '../providers';\nimport { PluginEndpointDiscovery } from '@backstage/backend-common';\n\nconst CLOCK_MARGIN_S = 10;\n\n/**\n * A identity client to interact with auth-backend\n * and authenticate backstage identity tokens\n *\n * @experimental This is not a stable API yet\n */\nexport class IdentityClient {\n private readonly discovery: PluginEndpointDiscovery;\n private readonly issuer: string;\n private keyStore: JWKS.KeyStore;\n private keyStoreUpdated: number;\n\n constructor(options: { discovery: PluginEndpointDiscovery; issuer: string }) {\n this.discovery = options.discovery;\n this.issuer = options.issuer;\n this.keyStore = new JWKS.KeyStore();\n this.keyStoreUpdated = 0;\n }\n\n /**\n * Verifies the given backstage identity token\n * Returns a BackstageIdentity (user) matching the token.\n * The method throws an error if verification fails.\n */\n async authenticate(token: string | undefined): Promise<BackstageIdentity> {\n // Extract token from header\n if (!token) {\n throw new Error('No token specified');\n }\n // Get signing key matching token\n const key = await this.getKey(token);\n if (!key) {\n throw new Error('No signing key matching token found');\n }\n // Verify token claims and signature\n // Note: Claims must match those set by TokenFactory when issuing tokens\n // Note: verify throws if verification fails\n const decoded = JWT.IdToken.verify(token, key, {\n algorithms: ['ES256'],\n audience: 'backstage',\n issuer: this.issuer,\n }) as { sub: string };\n // Verified, return the matching user as BackstageIdentity\n // TODO: Settle internal user format/properties\n const user: BackstageIdentity = {\n id: decoded.sub,\n idToken: token,\n };\n return user;\n }\n\n /**\n * Parses the given authorization header and returns\n * the bearer token, or null if no bearer token is given\n */\n static getBearerToken(\n authorizationHeader: string | undefined,\n ): string | undefined {\n if (typeof authorizationHeader !== 'string') {\n return undefined;\n }\n const matches = authorizationHeader.match(/Bearer\\s+(\\S+)/i);\n return matches?.[1];\n }\n\n /**\n * Returns the public signing key matching the given jwt token,\n * or null if no matching key was found\n */\n private async getKey(rawJwtToken: string): Promise<JWK.Key | null> {\n const { header, payload } = JWT.decode(rawJwtToken, {\n complete: true,\n }) as {\n header: { kid: string };\n payload: { iat: number };\n };\n\n // Refresh public keys if needed\n // Add a small margin in case clocks are out of sync\n const keyStoreHasKey = !!this.keyStore.get({ kid: header.kid });\n const issuedAfterLastRefresh =\n payload?.iat && payload.iat > this.keyStoreUpdated - CLOCK_MARGIN_S;\n if (!keyStoreHasKey && issuedAfterLastRefresh) {\n await this.refreshKeyStore();\n }\n\n return this.keyStore.get({ kid: header.kid });\n }\n\n /**\n * Lists public part of keys used to sign Backstage Identity tokens\n */\n async listPublicKeys(): Promise<{\n keys: JSONWebKey[];\n }> {\n const url = `${await this.discovery.getBaseUrl(\n 'auth',\n )}/.well-known/jwks.json`;\n const response = await fetch(url);\n\n if (!response.ok) {\n const payload = await response.text();\n const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`;\n throw new Error(message);\n }\n\n const publicKeys: { keys: JSONWebKey[] } = await response.json();\n\n return publicKeys;\n }\n\n /**\n * Fetches public keys and caches them locally\n */\n private async refreshKeyStore(): Promise<void> {\n const now = Date.now() / 1000;\n const publicKeys = await this.listPublicKeys();\n this.keyStore = JWKS.asKeyStore({\n keys: publicKeys.keys.map(key => key as JSONWebKey),\n });\n this.keyStoreUpdated = now;\n }\n}\n","/*\n * Copyright 2020 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 */\n\nimport { TokenIssuer, TokenParams, KeyStore, AnyJWK } from './types';\nimport { JSONWebKey, JWK, JWS } from 'jose';\nimport { Logger } from 'winston';\nimport { v4 as uuid } from 'uuid';\nimport { DateTime } from 'luxon';\n\nconst MS_IN_S = 1000;\n\ntype Options = {\n logger: Logger;\n /** Value of the issuer claim in issued tokens */\n issuer: string;\n /** Key store used for storing signing keys */\n keyStore: KeyStore;\n /** Expiration time of signing keys in seconds */\n keyDurationSeconds: number;\n};\n\n/**\n * A token issuer that is able to issue tokens in a distributed system\n * backed by a single database. Tokens are issued using lazily generated\n * signing keys, where each running instance of the auth service uses its own\n * signing key.\n *\n * The public parts of the keys are all stored in the shared key storage,\n * and any of the instances of the auth service will return the full list\n * of public keys that are currently in storage.\n *\n * Signing keys are automatically rotated at the same interval as the token\n * duration. Expired keys are kept in storage until there are no valid tokens\n * in circulation that could have been signed by that key.\n */\nexport class TokenFactory implements TokenIssuer {\n private readonly issuer: string;\n private readonly logger: Logger;\n private readonly keyStore: KeyStore;\n private readonly keyDurationSeconds: number;\n\n private keyExpiry?: Date;\n private privateKeyPromise?: Promise<JSONWebKey>;\n\n constructor(options: Options) {\n this.issuer = options.issuer;\n this.logger = options.logger;\n this.keyStore = options.keyStore;\n this.keyDurationSeconds = options.keyDurationSeconds;\n }\n\n async issueToken(params: TokenParams): Promise<string> {\n const key = await this.getKey();\n\n const iss = this.issuer;\n const sub = params.claims.sub;\n const ent = params.claims.ent;\n const aud = 'backstage';\n const iat = Math.floor(Date.now() / MS_IN_S);\n const exp = iat + this.keyDurationSeconds;\n\n this.logger.info(`Issuing token for ${sub}, with entities ${ent ?? []}`);\n\n return JWS.sign({ iss, sub, aud, iat, exp, ent }, key, {\n alg: key.alg,\n kid: key.kid,\n });\n }\n\n // This will be called by other services that want to verify ID tokens.\n // It is important that it returns a list of all public keys that could\n // have been used to sign tokens that have not yet expired.\n async listPublicKeys(): Promise<{ keys: AnyJWK[] }> {\n const { items: keys } = await this.keyStore.listKeys();\n\n const validKeys = [];\n const expiredKeys = [];\n\n for (const key of keys) {\n // Allow for a grace period of another full key duration before we remove the keys from the database\n const expireAt = DateTime.fromJSDate(key.createdAt).plus({\n seconds: 3 * this.keyDurationSeconds,\n });\n if (expireAt < DateTime.local()) {\n expiredKeys.push(key);\n } else {\n validKeys.push(key);\n }\n }\n\n // Lazily prune expired keys. This may cause duplicate removals if we have concurrent callers, but w/e\n if (expiredKeys.length > 0) {\n const kids = expiredKeys.map(({ key }) => key.kid);\n\n this.logger.info(`Removing expired signing keys, '${kids.join(\"', '\")}'`);\n\n // We don't await this, just let it run in the background\n this.keyStore.removeKeys(kids).catch(error => {\n this.logger.error(`Failed to remove expired keys, ${error}`);\n });\n }\n\n // NOTE: we're currently only storing public keys, but if we start storing private keys we'd have to convert here\n return { keys: validKeys.map(({ key }) => key) };\n }\n\n private async getKey(): Promise<JSONWebKey> {\n // Make sure that we only generate one key at a time\n if (this.privateKeyPromise) {\n if (\n this.keyExpiry &&\n DateTime.fromJSDate(this.keyExpiry) > DateTime.local()\n ) {\n return this.privateKeyPromise;\n }\n this.logger.info(`Signing key has expired, generating new key`);\n delete this.privateKeyPromise;\n }\n\n this.keyExpiry = DateTime.utc()\n .plus({\n seconds: this.keyDurationSeconds,\n })\n .toJSDate();\n const promise = (async () => {\n // This generates a new signing key to be used to sign tokens until the next key rotation\n const key = await JWK.generate('EC', 'P-256', {\n use: 'sig',\n kid: uuid(),\n alg: 'ES256',\n });\n\n // We're not allowed to use the key until it has been successfully stored\n // TODO: some token verification implementations aggressively cache the list of keys, and\n // don't attempt to fetch new ones even if they encounter an unknown kid. Therefore we\n // may want to keep using the existing key for some period of time until we switch to\n // the new one. This also needs to be implemented cross-service though, meaning new services\n // that boot up need to be able to grab an existing key to use for signing.\n this.logger.info(`Created new signing key ${key.kid}`);\n await this.keyStore.addKey(key.toJWK(false) as unknown as AnyJWK);\n\n // At this point we are allowed to start using the new key\n return key as JSONWebKey;\n })();\n\n this.privateKeyPromise = promise;\n\n try {\n // If we fail to generate a new key, we need to clear the state so that\n // the next caller will try to generate another key.\n await promise;\n } catch (error) {\n this.logger.error(`Failed to generate new signing key, ${error}`);\n delete this.keyExpiry;\n delete this.privateKeyPromise;\n }\n\n return promise;\n }\n}\n","/*\n * Copyright 2020 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 */\n\nimport { resolvePackagePath } from '@backstage/backend-common';\nimport { Knex } from 'knex';\nimport { DateTime } from 'luxon';\nimport { AnyJWK, KeyStore, StoredKey } from './types';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage/plugin-auth-backend',\n 'migrations',\n);\n\nconst TABLE = 'signing_keys';\n\ntype Row = {\n created_at: Date; // row.created_at is a string after being returned from the database\n kid: string;\n key: string;\n};\n\ntype Options = {\n database: Knex;\n};\n\nconst parseDate = (date: string | Date) => {\n const parsedDate =\n typeof date === 'string'\n ? DateTime.fromSQL(date, { zone: 'UTC' })\n : DateTime.fromJSDate(date);\n\n if (!parsedDate.isValid) {\n throw new Error(\n `Failed to parse date, reason: ${parsedDate.invalidReason}, explanation: ${parsedDate.invalidExplanation}`,\n );\n }\n\n return parsedDate.toJSDate();\n};\n\nexport class DatabaseKeyStore implements KeyStore {\n static async create(options: Options): Promise<DatabaseKeyStore> {\n const { database } = options;\n\n await database.migrate.latest({\n directory: migrationsDir,\n });\n\n return new DatabaseKeyStore(options);\n }\n\n private readonly database: Knex;\n\n private constructor(options: Options) {\n this.database = options.database;\n }\n\n async addKey(key: AnyJWK): Promise<void> {\n await this.database<Row>(TABLE).insert({\n kid: key.kid,\n key: JSON.stringify(key),\n });\n }\n\n async listKeys(): Promise<{ items: StoredKey[] }> {\n const rows = await this.database<Row>(TABLE).select();\n\n return {\n items: rows.map(row => ({\n key: JSON.parse(row.key),\n createdAt: parseDate(row.created_at),\n })),\n };\n }\n\n async removeKeys(kids: string[]): Promise<void> {\n await this.database(TABLE).delete().whereIn('kid', kids);\n }\n}\n","/*\n * Copyright 2020 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 */\n\nimport { KeyStore, AnyJWK, StoredKey } from './types';\nimport { DateTime } from 'luxon';\n\nexport class MemoryKeyStore implements KeyStore {\n private readonly keys = new Map<string, { createdAt: Date; key: string }>();\n\n async addKey(key: AnyJWK): Promise<void> {\n this.keys.set(key.kid, {\n createdAt: DateTime.utc().toJSDate(),\n key: JSON.stringify(key),\n });\n }\n\n async removeKeys(kids: string[]): Promise<void> {\n for (const kid of kids) {\n this.keys.delete(kid);\n }\n }\n\n async listKeys(): Promise<{ items: StoredKey[] }> {\n return {\n items: Array.from(this.keys).map(([, { createdAt, key: keyStr }]) => ({\n createdAt,\n key: JSON.parse(keyStr),\n })),\n };\n }\n}\n","/*\n * Copyright 2021 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 */\n\nimport { Logger } from 'winston';\nimport {\n DocumentData,\n Firestore,\n QuerySnapshot,\n Settings,\n WriteResult,\n} from '@google-cloud/firestore';\n\nimport { AnyJWK, KeyStore, StoredKey } from './types';\n\nexport type FirestoreKeyStoreSettings = Settings & Options;\n\ntype Options = {\n path?: string;\n timeout?: number;\n};\n\nexport const DEFAULT_TIMEOUT_MS = 10000;\nexport const DEFAULT_DOCUMENT_PATH = 'sessions';\n\nexport class FirestoreKeyStore implements KeyStore {\n static async create(\n settings?: FirestoreKeyStoreSettings,\n ): Promise<FirestoreKeyStore> {\n const { path, timeout, ...firestoreSettings } = settings ?? {};\n const database = new Firestore(firestoreSettings);\n\n return new FirestoreKeyStore(\n database,\n path ?? DEFAULT_DOCUMENT_PATH,\n timeout ?? DEFAULT_TIMEOUT_MS,\n );\n }\n\n private constructor(\n private readonly database: Firestore,\n private readonly path: string,\n private readonly timeout: number,\n ) {}\n\n static async verifyConnection(\n keyStore: FirestoreKeyStore,\n logger?: Logger,\n ): Promise<void> {\n try {\n await keyStore.verify();\n } catch (error) {\n if (process.env.NODE_ENV !== 'development') {\n throw new Error(\n `Failed to connect to database: ${(error as Error).message}`,\n );\n }\n logger?.warn(\n `Failed to connect to database: ${(error as Error).message}`,\n );\n }\n }\n\n async addKey(key: AnyJWK): Promise<void> {\n await this.withTimeout<WriteResult>(\n this.database\n .collection(this.path)\n .doc(key.kid)\n .set({\n kid: key.kid,\n key: JSON.stringify(key),\n }),\n );\n }\n\n async listKeys(): Promise<{ items: StoredKey[] }> {\n const keys = await this.withTimeout<QuerySnapshot<DocumentData>>(\n this.database.collection(this.path).get(),\n );\n\n return {\n items: keys.docs.map(key => ({\n key: key.data() as AnyJWK,\n createdAt: key.createTime.toDate(),\n })),\n };\n }\n\n async removeKeys(kids: string[]): Promise<void> {\n // This is probably really slow, but it's done async in the background\n for (const kid of kids) {\n await this.withTimeout<WriteResult>(\n this.database.collection(this.path).doc(kid).delete(),\n );\n }\n\n /**\n * This could be achieved with batching but there's a couple of limitations with that:\n *\n * - A batched write can contain a maximum of 500 operations\n * https://firebase.google.com/docs/firestore/manage-data/transactions#batched-writes\n *\n * - The \"in\" operator can combine a maximum of 10 equality clauses\n * https://firebase.google.com/docs/firestore/query-data/queries#in_not-in_and_array-contains-any\n *\n * Example:\n *\n * const batch = this.database.batch();\n * const docs = await this.database\n * .collection(this.path)\n * .where('kid', 'in', kids)\n * .get();\n * docs.forEach(doc => {\n * batch.delete(doc.ref);\n * });\n * await batch.commit();\n *\n */\n }\n\n /**\n * Helper function to allow us to modify the timeout used when\n * performing Firestore database operations.\n *\n * The reason for this is that it seems that there's no other\n * practical solution to change the default timeout of 10mins\n * that Firestore has.\n *\n */\n private async withTimeout<T>(operation: Promise<T>): Promise<T> {\n const timer = new Promise<never>((_, reject) =>\n setTimeout(() => {\n reject(new Error(`Operation timed out after ${this.timeout}ms`));\n }, this.timeout),\n );\n return Promise.race<T>([operation, timer]);\n }\n\n /**\n * Used to verify that the database is reachable.\n */\n private async verify(): Promise<void> {\n await this.withTimeout(this.database.collection(this.path).limit(1).get());\n }\n}\n","/*\n * Copyright 2021 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 */\n\nimport { Logger } from 'winston';\nimport { pickBy } from 'lodash';\n\nimport { PluginDatabaseManager } from '@backstage/backend-common';\nimport { Config } from '@backstage/config';\n\nimport { DatabaseKeyStore } from './DatabaseKeyStore';\nimport { MemoryKeyStore } from './MemoryKeyStore';\nimport { FirestoreKeyStore } from './FirestoreKeyStore';\nimport { KeyStore } from './types';\n\ntype Options = {\n logger?: Logger;\n database?: PluginDatabaseManager;\n};\n\nexport class KeyStores {\n /**\n * Looks at the `auth.keyStore` section in the application configuration\n * and returns a KeyStore store. Defaults to `database`\n *\n * @returns a KeyStore store\n */\n static async fromConfig(\n config: Config,\n options?: Options,\n ): Promise<KeyStore> {\n const { logger, database } = options ?? {};\n\n const ks = config.getOptionalConfig('auth.keyStore');\n const provider = ks?.getOptionalString('provider') ?? 'database';\n\n logger?.info(`Configuring \"${provider}\" as KeyStore provider`);\n\n if (provider === 'database') {\n if (!database) {\n throw new Error('This KeyStore provider requires a database');\n }\n\n return await DatabaseKeyStore.create({\n database: await database.getClient(),\n });\n }\n\n if (provider === 'memory') {\n return new MemoryKeyStore();\n }\n\n if (provider === 'firestore') {\n const settings = ks?.getConfig(provider);\n\n const keyStore = await FirestoreKeyStore.create(\n pickBy(\n {\n projectId: settings?.getOptionalString('projectId'),\n keyFilename: settings?.getOptionalString('keyFilename'),\n host: settings?.getOptionalString('host'),\n port: settings?.getOptionalNumber('port'),\n ssl: settings?.getOptionalBoolean('ssl'),\n path: settings?.getOptionalString('path'),\n timeout: settings?.getOptionalNumber('timeout'),\n },\n value => value !== undefined,\n ),\n );\n await FirestoreKeyStore.verifyConnection(keyStore, logger);\n\n return keyStore;\n }\n\n throw new Error(`Unknown KeyStore provider: ${provider}`);\n }\n}\n","/*\n * Copyright 2020 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 */\n\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport cookieParser from 'cookie-parser';\nimport { Logger } from 'winston';\nimport {\n defaultAuthProviderFactories,\n AuthProviderFactory,\n} from '../providers';\nimport {\n PluginDatabaseManager,\n PluginEndpointDiscovery,\n} from '@backstage/backend-common';\nimport { assertError, NotFoundError } from '@backstage/errors';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { Config } from '@backstage/config';\nimport { createOidcRouter, TokenFactory, KeyStores } from '../identity';\nimport session from 'express-session';\nimport passport from 'passport';\nimport { Minimatch } from 'minimatch';\n\ntype ProviderFactories = { [s: string]: AuthProviderFactory };\n\nexport interface RouterOptions {\n logger: Logger;\n database: PluginDatabaseManager;\n config: Config;\n discovery: PluginEndpointDiscovery;\n providerFactories?: ProviderFactories;\n}\n\nexport async function createRouter({\n logger,\n config,\n discovery,\n database,\n providerFactories,\n}: RouterOptions): Promise<express.Router> {\n const router = Router();\n\n const appUrl = config.getString('app.baseUrl');\n const authUrl = await discovery.getExternalBaseUrl('auth');\n\n const keyStore = await KeyStores.fromConfig(config, { logger, database });\n const keyDurationSeconds = 3600;\n\n const tokenIssuer = new TokenFactory({\n issuer: authUrl,\n keyStore,\n keyDurationSeconds,\n logger: logger.child({ component: 'token-factory' }),\n });\n const catalogApi = new CatalogClient({ discoveryApi: discovery });\n\n const secret = config.getOptionalString('auth.session.secret');\n if (secret) {\n router.use(cookieParser(secret));\n // TODO: Configure the server-side session storage. The default MemoryStore is not designed for production\n router.use(session({ secret, saveUninitialized: false, resave: false }));\n router.use(passport.initialize());\n router.use(passport.session());\n } else {\n router.use(cookieParser());\n }\n router.use(express.urlencoded({ extended: false }));\n router.use(express.json());\n\n const allProviderFactories = {\n ...defaultAuthProviderFactories,\n ...providerFactories,\n };\n const providersConfig = config.getConfig('auth.providers');\n const configuredProviders = providersConfig.keys();\n\n const isOriginAllowed = createOriginFilter(config);\n\n for (const [providerId, providerFactory] of Object.entries(\n allProviderFactories,\n )) {\n if (configuredProviders.includes(providerId)) {\n logger.info(`Configuring provider, ${providerId}`);\n try {\n const provider = providerFactory({\n providerId,\n globalConfig: { baseUrl: authUrl, appUrl, isOriginAllowed },\n config: providersConfig.getConfig(providerId),\n logger,\n tokenIssuer,\n discovery,\n catalogApi,\n });\n\n const r = Router();\n\n r.get('/start', provider.start.bind(provider));\n r.get('/handler/frame', provider.frameHandler.bind(provider));\n r.post('/handler/frame', provider.frameHandler.bind(provider));\n if (provider.logout) {\n r.post('/logout', provider.logout.bind(provider));\n }\n if (provider.refresh) {\n r.get('/refresh', provider.refresh.bind(provider));\n }\n\n router.use(`/${providerId}`, r);\n } catch (e) {\n assertError(e);\n if (process.env.NODE_ENV !== 'development') {\n throw new Error(\n `Failed to initialize ${providerId} auth provider, ${e.message}`,\n );\n }\n\n logger.warn(`Skipping ${providerId} auth provider, ${e.message}`);\n\n router.use(`/${providerId}`, () => {\n // If the user added the provider under auth.providers but the clientId and clientSecret etc. were not found.\n throw new NotFoundError(\n `Auth provider registered for '${providerId}' is misconfigured. This could mean the configs under ` +\n `auth.providers.${providerId} are missing or the environment variables used are not defined. ` +\n `Check the auth backend plugin logs when the backend starts to see more details.`,\n );\n });\n }\n } else {\n router.use(`/${providerId}`, () => {\n throw new NotFoundError(\n `No auth provider registered for '${providerId}'`,\n );\n });\n }\n }\n\n router.use(\n createOidcRouter({\n tokenIssuer,\n baseUrl: authUrl,\n }),\n );\n\n router.use('/:provider/', req => {\n const { provider } = req.params;\n throw new NotFoundError(`Unknown auth provider '${provider}'`);\n });\n\n return router;\n}\n\nexport function createOriginFilter(\n config: Config,\n): (origin: string) => boolean {\n const appUrl = config.getString('app.baseUrl');\n const { origin: appOrigin } = new URL(appUrl);\n\n const allowedOrigins = config.getOptionalStringArray(\n 'auth.experimentalExtraAllowedOrigins',\n );\n\n const allowedOriginPatterns =\n allowedOrigins?.map(\n pattern => new Minimatch(pattern, { nocase: true, noglobstar: true }),\n ) ?? [];\n\n return origin => {\n if (origin === appOrigin) {\n return true;\n }\n return allowedOriginPatterns.some(pattern => pattern.match(origin));\n };\n}\n"],"names":["jwtDecoder","pickBy","InputError","crypto","URL","url","NotAllowedError","isError","ConflictError","NotFoundError","parseEntityRef","stringifyEntityRef","RELATION_MEMBER_OF","GithubStrategy","GitlabStrategy","GoogleStrategy","MicrosoftStrategy","OAuth2Strategy","OktaStrategy","BitbucketStrategy","InternalOAuthError","NodeCache","AuthenticationError","JWT","fetch","Issuer","OidcStrategy","SamlStrategy","OneLoginStrategy","Router","JWKS","JWS","DateTime","JWK","uuid","resolvePackagePath","Firestore","CatalogClient","cookieParser","session","passport","express","defaultAuthProviderFactories","Minimatch"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/plugin-auth-backend",
3
3
  "description": "A Backstage backend plugin that handles authentication",
4
- "version": "0.4.6",
4
+ "version": "0.4.7",
5
5
  "main": "dist/index.cjs.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "Apache-2.0",
@@ -30,12 +30,12 @@
30
30
  "clean": "backstage-cli clean"
31
31
  },
32
32
  "dependencies": {
33
- "@backstage/backend-common": "^0.9.8",
34
- "@backstage/catalog-client": "^0.5.0",
33
+ "@backstage/backend-common": "^0.9.9",
34
+ "@backstage/catalog-client": "^0.5.1",
35
35
  "@backstage/catalog-model": "^0.9.6",
36
36
  "@backstage/config": "^0.1.11",
37
37
  "@backstage/errors": "^0.1.4",
38
- "@backstage/test-utils": "^0.1.20",
38
+ "@backstage/test-utils": "^0.1.21",
39
39
  "@google-cloud/firestore": "^4.15.1",
40
40
  "@types/express": "^4.17.6",
41
41
  "@types/passport": "^1.0.3",
@@ -73,7 +73,7 @@
73
73
  "yn": "^4.0.0"
74
74
  },
75
75
  "devDependencies": {
76
- "@backstage/cli": "^0.8.1",
76
+ "@backstage/cli": "^0.8.2",
77
77
  "@types/body-parser": "^1.19.0",
78
78
  "@types/cookie-parser": "^1.4.2",
79
79
  "@types/express-session": "^1.17.2",
@@ -92,5 +92,5 @@
92
92
  "config.d.ts"
93
93
  ],
94
94
  "configSchema": "config.d.ts",
95
- "gitHead": "3db0cb3683d3000666802af90a465ba4fb0d1e8d"
95
+ "gitHead": "5bdaccc40b4a814cf0b45d429f15a3afacc2f60b"
96
96
  }