You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@backstage/plugin-auth-node

Package Overview
Dependencies
Maintainers
3
Versions
1396
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@backstage/plugin-auth-node - npm Package Compare versions

Comparing version
0.0.0-nightly-20260127025640
to
0.0.0-nightly-20260128025246
+17
-2
CHANGELOG.md
# @backstage/plugin-auth-node
## 0.0.0-nightly-20260127025640
## 0.0.0-nightly-20260128025246
### Patch Changes
- 7455dae: Use node prefix on native imports
- 69d880e: Bump to latest zod to ensure it has the latest features
- Updated dependencies
- @backstage/backend-plugin-api@0.0.0-nightly-20260127025640
- @backstage/backend-plugin-api@0.0.0-nightly-20260128025246
- @backstage/catalog-client@1.12.1

@@ -16,2 +17,16 @@ - @backstage/catalog-model@1.7.6

## 0.6.12-next.0
### Patch Changes
- 7455dae: Use node prefix on native imports
- 69d880e: Bump to latest zod to ensure it has the latest features
- Updated dependencies
- @backstage/backend-plugin-api@1.7.0-next.0
- @backstage/catalog-client@1.12.1
- @backstage/catalog-model@1.7.6
- @backstage/config@1.3.6
- @backstage/errors@1.2.7
- @backstage/types@1.2.2
## 0.6.11

@@ -18,0 +33,0 @@

+1
-1
'use strict';
var crypto = require('crypto');
var crypto = require('node:crypto');
var errors = require('@backstage/errors');

@@ -5,0 +5,0 @@

@@ -1,1 +0,1 @@

{"version":3,"file":"sendWebMessageResponse.cjs.js","sources":["../../src/flow/sendWebMessageResponse.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 { Response } from 'express';\nimport crypto from 'crypto';\nimport { ClientAuthResponse } from '../types';\nimport { serializeError } from '@backstage/errors';\n\n/**\n * Payload sent as a post message after the auth request is complete.\n * If successful then has a valid payload with Auth information else contains an error.\n *\n * @public\n */\nexport type WebMessageResponse =\n | {\n type: 'authorization_response';\n response: ClientAuthResponse<unknown>;\n }\n | {\n type: 'authorization_response';\n error: Error;\n };\n\n/** @internal */\nexport function safelyEncodeURIComponent(value: string): 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\n/** @public */\nexport function sendWebMessageResponse(\n res: Response,\n appOrigin: string,\n response: WebMessageResponse,\n): void {\n const jsonData = JSON.stringify(response, (_, value) => {\n if (value instanceof Error) {\n return serializeError(value);\n }\n return value;\n });\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"],"names":["serializeError","crypto"],"mappings":";;;;;;;;;AAsCO,SAAS,yBAAyB,KAAA,EAAuB;AAG9D,EAAA,OAAO,kBAAA,CAAmB,KAAK,CAAA,CAAE,OAAA,CAAQ,MAAM,KAAK,CAAA;AACtD;AAGO,SAAS,sBAAA,CACd,GAAA,EACA,SAAA,EACA,QAAA,EACM;AACN,EAAA,MAAM,WAAW,IAAA,CAAK,SAAA,CAAU,QAAA,EAAU,CAAC,GAAG,KAAA,KAAU;AACtD,IAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,MAAA,OAAOA,sBAAe,KAAK,CAAA;AAAA,IAC7B;AACA,IAAA,OAAO,KAAA;AAAA,EACT,CAAC,CAAA;AACD,EAAA,MAAM,UAAA,GAAa,yBAAyB,QAAQ,CAAA;AACpD,EAAA,MAAM,YAAA,GAAe,yBAAyB,SAAS,CAAA;AAmBvD,EAAA,MAAM,MAAA,GAAS;AAAA,2CAAA,EAC4B,UAAU,CAAA;AAAA,qCAAA,EAChB,YAAY,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA,CAAA;AAQjD,EAAA,MAAM,IAAA,GAAOC,wBAAO,UAAA,CAAW,QAAQ,EAAE,MAAA,CAAO,MAAM,CAAA,CAAE,MAAA,CAAO,QAAQ,CAAA;AAEvE,EAAA,GAAA,CAAI,SAAA,CAAU,gBAAgB,WAAW,CAAA;AACzC,EAAA,GAAA,CAAI,SAAA,CAAU,mBAAmB,YAAY,CAAA;AAC7C,EAAA,GAAA,CAAI,SAAA,CAAU,yBAAA,EAA2B,CAAA,mBAAA,EAAsB,IAAI,CAAA,CAAA,CAAG,CAAA;AACtE,EAAA,GAAA,CAAI,GAAA,CAAI,CAAA,oBAAA,EAAuB,MAAM,CAAA,wBAAA,CAAyB,CAAA;AAChE;;;;;"}
{"version":3,"file":"sendWebMessageResponse.cjs.js","sources":["../../src/flow/sendWebMessageResponse.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 { Response } from 'express';\nimport crypto from 'node:crypto';\nimport { ClientAuthResponse } from '../types';\nimport { serializeError } from '@backstage/errors';\n\n/**\n * Payload sent as a post message after the auth request is complete.\n * If successful then has a valid payload with Auth information else contains an error.\n *\n * @public\n */\nexport type WebMessageResponse =\n | {\n type: 'authorization_response';\n response: ClientAuthResponse<unknown>;\n }\n | {\n type: 'authorization_response';\n error: Error;\n };\n\n/** @internal */\nexport function safelyEncodeURIComponent(value: string): 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\n/** @public */\nexport function sendWebMessageResponse(\n res: Response,\n appOrigin: string,\n response: WebMessageResponse,\n): void {\n const jsonData = JSON.stringify(response, (_, value) => {\n if (value instanceof Error) {\n return serializeError(value);\n }\n return value;\n });\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"],"names":["serializeError","crypto"],"mappings":";;;;;;;;;AAsCO,SAAS,yBAAyB,KAAA,EAAuB;AAG9D,EAAA,OAAO,kBAAA,CAAmB,KAAK,CAAA,CAAE,OAAA,CAAQ,MAAM,KAAK,CAAA;AACtD;AAGO,SAAS,sBAAA,CACd,GAAA,EACA,SAAA,EACA,QAAA,EACM;AACN,EAAA,MAAM,WAAW,IAAA,CAAK,SAAA,CAAU,QAAA,EAAU,CAAC,GAAG,KAAA,KAAU;AACtD,IAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,MAAA,OAAOA,sBAAe,KAAK,CAAA;AAAA,IAC7B;AACA,IAAA,OAAO,KAAA;AAAA,EACT,CAAC,CAAA;AACD,EAAA,MAAM,UAAA,GAAa,yBAAyB,QAAQ,CAAA;AACpD,EAAA,MAAM,YAAA,GAAe,yBAAyB,SAAS,CAAA;AAmBvD,EAAA,MAAM,MAAA,GAAS;AAAA,2CAAA,EAC4B,UAAU,CAAA;AAAA,qCAAA,EAChB,YAAY,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA,CAAA;AAQjD,EAAA,MAAM,IAAA,GAAOC,wBAAO,UAAA,CAAW,QAAQ,EAAE,MAAA,CAAO,MAAM,CAAA,CAAE,MAAA,CAAO,QAAQ,CAAA;AAEvE,EAAA,GAAA,CAAI,SAAA,CAAU,gBAAgB,WAAW,CAAA;AACzC,EAAA,GAAA,CAAI,SAAA,CAAU,mBAAmB,YAAY,CAAA;AAC7C,EAAA,GAAA,CAAI,SAAA,CAAU,yBAAA,EAA2B,CAAA,mBAAA,EAAsB,IAAI,CAAA,CAAA,CAAG,CAAA;AACtE,EAAA,GAAA,CAAI,GAAA,CAAI,CAAA,oBAAA,EAAuB,MAAM,CAAA,wBAAA,CAAyB,CAAA;AAChE;;;;;"}
'use strict';
var crypto = require('crypto');
var url = require('url');
var crypto = require('node:crypto');
var node_url = require('node:url');
var errors = require('@backstage/errors');

@@ -30,3 +30,3 @@ var state = require('./state.cjs.js');

} = options;
const defaultAppOrigin = new url.URL(appUrl).origin;
const defaultAppOrigin = new node_url.URL(appUrl).origin;
const callbackUrl = config$1.getOptionalString("callbackUrl") ?? `${baseUrl}/${providerId}/handler/frame`;

@@ -85,3 +85,3 @@ const sessionDuration = config$1.has("sessionDuration") ? config.readDurationFromConfig(config$1, { key: "sessionDuration" }) : void 0;

try {
origin = new url.URL(state$1.origin).origin;
origin = new node_url.URL(state$1.origin).origin;
} catch {

@@ -148,3 +148,3 @@ throw new errors.NotAllowedError("App origin is invalid, failed to parse");

if (state$1?.flow === "redirect" && state$1?.redirectUrl) {
const redirectUrl = new url.URL(state$1.redirectUrl);
const redirectUrl = new node_url.URL(state$1.redirectUrl);
redirectUrl.searchParams.set("error", message);

@@ -151,0 +151,0 @@ res.redirect(redirectUrl.toString());

@@ -1,1 +0,1 @@

{"version":3,"file":"createOAuthRouteHandlers.cjs.js","sources":["../../src/oauth/createOAuthRouteHandlers.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 crypto from 'crypto';\nimport { URL } from 'url';\nimport {\n AuthenticationError,\n InputError,\n isError,\n NotAllowedError,\n} from '@backstage/errors';\nimport {\n encodeOAuthState,\n decodeOAuthState,\n OAuthStateTransform,\n} from './state';\nimport { sendWebMessageResponse } from '../flow';\nimport { prepareBackstageIdentityResponse } from '../identity';\nimport { OAuthCookieManager } from './OAuthCookieManager';\nimport {\n AuthProviderRouteHandlers,\n AuthResolverContext,\n ClientAuthResponse,\n CookieConfigurer,\n ProfileTransform,\n SignInResolver,\n} from '../types';\nimport { OAuthAuthenticator, OAuthAuthenticatorResult } from './types';\nimport { Config, readDurationFromConfig } from '@backstage/config';\nimport { CookieScopeManager } from './CookieScopeManager';\n\n/** @public */\nexport interface OAuthRouteHandlersOptions<TProfile> {\n authenticator: OAuthAuthenticator<any, TProfile>;\n appUrl: string;\n baseUrl: string;\n isOriginAllowed: (origin: string) => boolean;\n providerId: string;\n config: Config;\n resolverContext: AuthResolverContext;\n additionalScopes?: string[];\n stateTransform?: OAuthStateTransform;\n profileTransform?: ProfileTransform<OAuthAuthenticatorResult<TProfile>>;\n cookieConfigurer?: CookieConfigurer;\n signInResolver?: SignInResolver<OAuthAuthenticatorResult<TProfile>>;\n}\n\n/** @internal */\ntype ClientOAuthResponse = ClientAuthResponse<{\n /**\n * An access token issued for the signed in user.\n */\n accessToken: string;\n /**\n * (Optional) Id token issued for the signed in user.\n */\n idToken?: string;\n /**\n * Expiry of the access token in seconds.\n */\n expiresInSeconds?: number;\n /**\n * Scopes granted for the access token.\n */\n scope: string;\n}>;\n\n/** @public */\nexport function createOAuthRouteHandlers<TProfile>(\n options: OAuthRouteHandlersOptions<TProfile>,\n): AuthProviderRouteHandlers {\n const {\n authenticator,\n config,\n baseUrl,\n appUrl,\n providerId,\n isOriginAllowed,\n cookieConfigurer,\n resolverContext,\n signInResolver,\n } = options;\n\n const defaultAppOrigin = new URL(appUrl).origin;\n const callbackUrl =\n config.getOptionalString('callbackUrl') ??\n `${baseUrl}/${providerId}/handler/frame`;\n const sessionDuration = config.has('sessionDuration')\n ? readDurationFromConfig(config, { key: 'sessionDuration' })\n : undefined;\n\n const stateTransform = options.stateTransform ?? (state => ({ state }));\n const profileTransform =\n options.profileTransform ?? authenticator.defaultProfileTransform;\n const authenticatorCtx = authenticator.initialize({ config, callbackUrl });\n const cookieManager = new OAuthCookieManager({\n baseUrl,\n callbackUrl,\n defaultAppOrigin,\n providerId,\n cookieConfigurer,\n sessionDuration,\n });\n\n const scopeManager = CookieScopeManager.create({\n config,\n authenticator,\n cookieManager,\n additionalScopes: options.additionalScopes,\n });\n\n return {\n async start(\n this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const env = req.query.env?.toString();\n const origin = req.query.origin?.toString();\n const redirectUrl = req.query.redirectUrl?.toString();\n const flow = req.query.flow?.toString();\n\n if (!env) {\n throw new InputError('No env provided in request query parameters');\n }\n\n const nonce = crypto.randomBytes(16).toString('base64');\n // set a nonce cookie before redirecting to oauth provider\n cookieManager.setNonce(res, nonce, origin);\n\n const { scope, scopeState } = await scopeManager.start(req);\n\n const state = { nonce, env, origin, redirectUrl, flow, ...scopeState };\n const { state: transformedState } = await stateTransform(state, { req });\n\n const { url, status } = await options.authenticator.start(\n {\n req,\n scope,\n state: encodeOAuthState(transformedState),\n },\n authenticatorCtx,\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 this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n let origin = defaultAppOrigin;\n let state;\n\n try {\n state = decodeOAuthState(req.query.state?.toString() ?? '');\n\n if (state.origin) {\n try {\n origin = new URL(state.origin).origin;\n } catch {\n throw new NotAllowedError('App origin is invalid, failed to parse');\n }\n if (!isOriginAllowed(origin)) {\n throw new NotAllowedError(`Origin '${origin}' is not allowed`);\n }\n }\n\n // The same nonce is passed through cookie and state, and they must match\n const cookieNonce = cookieManager.getNonce(req);\n const stateNonce = state.nonce;\n if (!cookieNonce) {\n throw new NotAllowedError('Auth response is missing cookie nonce');\n }\n if (cookieNonce !== stateNonce) {\n throw new NotAllowedError('Invalid nonce');\n }\n\n const result = await authenticator.authenticate(\n { req },\n authenticatorCtx,\n );\n const { profile } = await profileTransform(result, resolverContext);\n\n const signInResult =\n signInResolver &&\n (await signInResolver({ profile, result }, resolverContext));\n\n const grantedScopes = await scopeManager.handleCallback(req, {\n result,\n state,\n origin,\n });\n\n const response: ClientOAuthResponse = {\n profile,\n providerInfo: {\n idToken: result.session.idToken,\n accessToken: result.session.accessToken,\n scope: grantedScopes,\n expiresInSeconds: result.session.expiresInSeconds,\n },\n ...(signInResult && {\n backstageIdentity: prepareBackstageIdentityResponse(signInResult),\n }),\n };\n\n if (result.session.refreshToken) {\n // set new refresh token\n cookieManager.setRefreshToken(\n res,\n result.session.refreshToken,\n origin,\n );\n }\n\n // When using the redirect flow we rely on refresh token we just\n // acquired to get a new session once we're back in the app.\n if (state.flow === 'redirect') {\n if (!state.redirectUrl) {\n throw new InputError(\n 'No redirectUrl provided in request query parameters',\n );\n }\n res.redirect(state.redirectUrl);\n return;\n }\n\n // post message back to popup if successful\n sendWebMessageResponse(res, origin, {\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\n if (state?.flow === 'redirect' && state?.redirectUrl) {\n const redirectUrl = new URL(state.redirectUrl);\n redirectUrl.searchParams.set('error', message);\n\n // set the error in a cookie and redirect user back to sign in where the error can be rendered\n res.redirect(redirectUrl.toString());\n } else {\n // post error message back to popup if failure\n sendWebMessageResponse(res, origin, {\n type: 'authorization_response',\n error: { name, message },\n });\n }\n }\n },\n\n async logout(\n this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n // We use this as a lightweight CSRF protection\n if (req.header('X-Requested-With') !== 'XMLHttpRequest') {\n throw new AuthenticationError('Invalid X-Requested-With header');\n }\n\n if (authenticator.logout) {\n const refreshToken = cookieManager.getRefreshToken(req);\n await authenticator.logout({ req, refreshToken }, authenticatorCtx);\n }\n\n // remove refresh token cookie if it is set\n cookieManager.removeRefreshToken(res, req.get('origin'));\n\n // remove persisted scopes\n await scopeManager.clear(req);\n\n res.status(200).end();\n },\n\n async refresh(\n this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n // We use this as a lightweight CSRF protection\n if (req.header('X-Requested-With') !== 'XMLHttpRequest') {\n throw new AuthenticationError('Invalid X-Requested-With header');\n }\n\n try {\n const refreshToken = cookieManager.getRefreshToken(req);\n\n // throw error if refresh token is missing in the request\n if (!refreshToken) {\n throw new InputError('Missing session cookie');\n }\n\n const scopeRefresh = await scopeManager.refresh(req);\n\n const result = await authenticator.refresh(\n {\n req,\n scope: scopeRefresh.scope,\n scopeAlreadyGranted: scopeRefresh.scopeAlreadyGranted,\n refreshToken,\n },\n authenticatorCtx,\n );\n\n const grantedScope = await scopeRefresh.commit(result);\n\n const { profile } = await profileTransform(result, resolverContext);\n\n const newRefreshToken = result.session.refreshToken;\n if (newRefreshToken && newRefreshToken !== refreshToken) {\n cookieManager.setRefreshToken(\n res,\n newRefreshToken,\n req.get('origin'),\n );\n }\n\n const response: ClientOAuthResponse = {\n profile,\n providerInfo: {\n idToken: result.session.idToken,\n accessToken: result.session.accessToken,\n scope: grantedScope,\n expiresInSeconds: result.session.expiresInSeconds,\n },\n };\n\n if (signInResolver) {\n const identity = await signInResolver(\n { profile, result },\n resolverContext,\n );\n response.backstageIdentity =\n prepareBackstageIdentityResponse(identity);\n }\n\n res.status(200).json(response);\n } catch (error) {\n throw new AuthenticationError('Refresh failed', error);\n }\n },\n };\n}\n"],"names":["config","URL","readDurationFromConfig","OAuthCookieManager","CookieScopeManager","InputError","crypto","state","encodeOAuthState","decodeOAuthState","NotAllowedError","prepareBackstageIdentityResponse","sendWebMessageResponse","isError","AuthenticationError"],"mappings":";;;;;;;;;;;;;;;;;AAkFO,SAAS,yBACd,OAAA,EAC2B;AAC3B,EAAA,MAAM;AAAA,IACJ,aAAA;AAAA,YACAA,QAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA,eAAA;AAAA,IACA,gBAAA;AAAA,IACA,eAAA;AAAA,IACA;AAAA,GACF,GAAI,OAAA;AAEJ,EAAA,MAAM,gBAAA,GAAmB,IAAIC,OAAA,CAAI,MAAM,CAAA,CAAE,MAAA;AACzC,EAAA,MAAM,WAAA,GACJD,SAAO,iBAAA,CAAkB,aAAa,KACtC,CAAA,EAAG,OAAO,IAAI,UAAU,CAAA,cAAA,CAAA;AAC1B,EAAA,MAAM,eAAA,GAAkBA,QAAA,CAAO,GAAA,CAAI,iBAAiB,CAAA,GAChDE,6BAAA,CAAuBF,QAAA,EAAQ,EAAE,GAAA,EAAK,iBAAA,EAAmB,CAAA,GACzD,MAAA;AAEJ,EAAA,MAAM,cAAA,GAAiB,OAAA,CAAQ,cAAA,KAAmB,CAAA,KAAA,MAAU,EAAE,KAAA,EAAM,CAAA,CAAA;AACpE,EAAA,MAAM,gBAAA,GACJ,OAAA,CAAQ,gBAAA,IAAoB,aAAA,CAAc,uBAAA;AAC5C,EAAA,MAAM,mBAAmB,aAAA,CAAc,UAAA,CAAW,UAAEA,QAAA,EAAQ,aAAa,CAAA;AACzE,EAAA,MAAM,aAAA,GAAgB,IAAIG,qCAAA,CAAmB;AAAA,IAC3C,OAAA;AAAA,IACA,WAAA;AAAA,IACA,gBAAA;AAAA,IACA,UAAA;AAAA,IACA,gBAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,MAAM,YAAA,GAAeC,sCAAmB,MAAA,CAAO;AAAA,YAC7CJ,QAAA;AAAA,IACA,aAAA;AAAA,IACA,aAAA;AAAA,IACA,kBAAkB,OAAA,CAAQ;AAAA,GAC3B,CAAA;AAED,EAAA,OAAO;AAAA,IACL,MAAM,KAAA,CAEJ,GAAA,EACA,GAAA,EACe;AACf,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,KAAA,CAAM,GAAA,EAAK,QAAA,EAAS;AACpC,MAAA,MAAM,MAAA,GAAS,GAAA,CAAI,KAAA,CAAM,MAAA,EAAQ,QAAA,EAAS;AAC1C,MAAA,MAAM,WAAA,GAAc,GAAA,CAAI,KAAA,CAAM,WAAA,EAAa,QAAA,EAAS;AACpD,MAAA,MAAM,IAAA,GAAO,GAAA,CAAI,KAAA,CAAM,IAAA,EAAM,QAAA,EAAS;AAEtC,MAAA,IAAI,CAAC,GAAA,EAAK;AACR,QAAA,MAAM,IAAIK,kBAAW,6CAA6C,CAAA;AAAA,MACpE;AAEA,MAAA,MAAM,QAAQC,uBAAA,CAAO,WAAA,CAAY,EAAE,CAAA,CAAE,SAAS,QAAQ,CAAA;AAEtD,MAAA,aAAA,CAAc,QAAA,CAAS,GAAA,EAAK,KAAA,EAAO,MAAM,CAAA;AAEzC,MAAA,MAAM,EAAE,KAAA,EAAO,UAAA,KAAe,MAAM,YAAA,CAAa,MAAM,GAAG,CAAA;AAE1D,MAAA,MAAMC,OAAA,GAAQ,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,WAAA,EAAa,IAAA,EAAM,GAAG,UAAA,EAAW;AACrE,MAAA,MAAM,EAAE,OAAO,gBAAA,EAAiB,GAAI,MAAM,cAAA,CAAeA,OAAA,EAAO,EAAE,GAAA,EAAK,CAAA;AAEvE,MAAA,MAAM,EAAE,GAAA,EAAK,MAAA,EAAO,GAAI,MAAM,QAAQ,aAAA,CAAc,KAAA;AAAA,QAClD;AAAA,UACE,GAAA;AAAA,UACA,KAAA;AAAA,UACA,KAAA,EAAOC,uBAAiB,gBAAgB;AAAA,SAC1C;AAAA,QACA;AAAA,OACF;AAEA,MAAA,GAAA,CAAI,aAAa,MAAA,IAAU,GAAA;AAC3B,MAAA,GAAA,CAAI,SAAA,CAAU,YAAY,GAAG,CAAA;AAC7B,MAAA,GAAA,CAAI,SAAA,CAAU,kBAAkB,GAAG,CAAA;AACnC,MAAA,GAAA,CAAI,GAAA,EAAI;AAAA,IACV,CAAA;AAAA,IAEA,MAAM,YAAA,CAEJ,GAAA,EACA,GAAA,EACe;AACf,MAAA,IAAI,MAAA,GAAS,gBAAA;AACb,MAAA,IAAID,OAAA;AAEJ,MAAA,IAAI;AACF,QAAAA,OAAA,GAAQE,uBAAiB,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,QAAA,MAAc,EAAE,CAAA;AAE1D,QAAA,IAAIF,QAAM,MAAA,EAAQ;AAChB,UAAA,IAAI;AACF,YAAA,MAAA,GAAS,IAAIN,OAAA,CAAIM,OAAA,CAAM,MAAM,CAAA,CAAE,MAAA;AAAA,UACjC,CAAA,CAAA,MAAQ;AACN,YAAA,MAAM,IAAIG,uBAAgB,wCAAwC,CAAA;AAAA,UACpE;AACA,UAAA,IAAI,CAAC,eAAA,CAAgB,MAAM,CAAA,EAAG;AAC5B,YAAA,MAAM,IAAIA,sBAAA,CAAgB,CAAA,QAAA,EAAW,MAAM,CAAA,gBAAA,CAAkB,CAAA;AAAA,UAC/D;AAAA,QACF;AAGA,QAAA,MAAM,WAAA,GAAc,aAAA,CAAc,QAAA,CAAS,GAAG,CAAA;AAC9C,QAAA,MAAM,aAAaH,OAAA,CAAM,KAAA;AACzB,QAAA,IAAI,CAAC,WAAA,EAAa;AAChB,UAAA,MAAM,IAAIG,uBAAgB,uCAAuC,CAAA;AAAA,QACnE;AACA,QAAA,IAAI,gBAAgB,UAAA,EAAY;AAC9B,UAAA,MAAM,IAAIA,uBAAgB,eAAe,CAAA;AAAA,QAC3C;AAEA,QAAA,MAAM,MAAA,GAAS,MAAM,aAAA,CAAc,YAAA;AAAA,UACjC,EAAE,GAAA,EAAI;AAAA,UACN;AAAA,SACF;AACA,QAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,MAAM,gBAAA,CAAiB,QAAQ,eAAe,CAAA;AAElE,QAAA,MAAM,YAAA,GACJ,kBACC,MAAM,cAAA,CAAe,EAAE,OAAA,EAAS,MAAA,IAAU,eAAe,CAAA;AAE5D,QAAA,MAAM,aAAA,GAAgB,MAAM,YAAA,CAAa,cAAA,CAAe,GAAA,EAAK;AAAA,UAC3D,MAAA;AAAA,iBACAH,OAAA;AAAA,UACA;AAAA,SACD,CAAA;AAED,QAAA,MAAM,QAAA,GAAgC;AAAA,UACpC,OAAA;AAAA,UACA,YAAA,EAAc;AAAA,YACZ,OAAA,EAAS,OAAO,OAAA,CAAQ,OAAA;AAAA,YACxB,WAAA,EAAa,OAAO,OAAA,CAAQ,WAAA;AAAA,YAC5B,KAAA,EAAO,aAAA;AAAA,YACP,gBAAA,EAAkB,OAAO,OAAA,CAAQ;AAAA,WACnC;AAAA,UACA,GAAI,YAAA,IAAgB;AAAA,YAClB,iBAAA,EAAmBI,kEAAiC,YAAY;AAAA;AAClE,SACF;AAEA,QAAA,IAAI,MAAA,CAAO,QAAQ,YAAA,EAAc;AAE/B,UAAA,aAAA,CAAc,eAAA;AAAA,YACZ,GAAA;AAAA,YACA,OAAO,OAAA,CAAQ,YAAA;AAAA,YACf;AAAA,WACF;AAAA,QACF;AAIA,QAAA,IAAIJ,OAAA,CAAM,SAAS,UAAA,EAAY;AAC7B,UAAA,IAAI,CAACA,QAAM,WAAA,EAAa;AACtB,YAAA,MAAM,IAAIF,iBAAA;AAAA,cACR;AAAA,aACF;AAAA,UACF;AACA,UAAA,GAAA,CAAI,QAAA,CAASE,QAAM,WAAW,CAAA;AAC9B,UAAA;AAAA,QACF;AAGA,QAAAK,6CAAA,CAAuB,KAAK,MAAA,EAAQ;AAAA,UAClC,IAAA,EAAM,wBAAA;AAAA,UACN;AAAA,SACD,CAAA;AAAA,MACH,SAAS,KAAA,EAAO;AACd,QAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAQ,GAAIC,cAAA,CAAQ,KAAK,CAAA,GACnC,KAAA,GACA,IAAI,KAAA,CAAM,2BAA2B,CAAA;AAEzC,QAAA,IAAIN,OAAA,EAAO,IAAA,KAAS,UAAA,IAAcA,OAAA,EAAO,WAAA,EAAa;AACpD,UAAA,MAAM,WAAA,GAAc,IAAIN,OAAA,CAAIM,OAAA,CAAM,WAAW,CAAA;AAC7C,UAAA,WAAA,CAAY,YAAA,CAAa,GAAA,CAAI,OAAA,EAAS,OAAO,CAAA;AAG7C,UAAA,GAAA,CAAI,QAAA,CAAS,WAAA,CAAY,QAAA,EAAU,CAAA;AAAA,QACrC,CAAA,MAAO;AAEL,UAAAK,6CAAA,CAAuB,KAAK,MAAA,EAAQ;AAAA,YAClC,IAAA,EAAM,wBAAA;AAAA,YACN,KAAA,EAAO,EAAE,IAAA,EAAM,OAAA;AAAQ,WACxB,CAAA;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAA;AAAA,IAEA,MAAM,MAAA,CAEJ,GAAA,EACA,GAAA,EACe;AAEf,MAAA,IAAI,GAAA,CAAI,MAAA,CAAO,kBAAkB,CAAA,KAAM,gBAAA,EAAkB;AACvD,QAAA,MAAM,IAAIE,2BAAoB,iCAAiC,CAAA;AAAA,MACjE;AAEA,MAAA,IAAI,cAAc,MAAA,EAAQ;AACxB,QAAA,MAAM,YAAA,GAAe,aAAA,CAAc,eAAA,CAAgB,GAAG,CAAA;AACtD,QAAA,MAAM,cAAc,MAAA,CAAO,EAAE,GAAA,EAAK,YAAA,IAAgB,gBAAgB,CAAA;AAAA,MACpE;AAGA,MAAA,aAAA,CAAc,kBAAA,CAAmB,GAAA,EAAK,GAAA,CAAI,GAAA,CAAI,QAAQ,CAAC,CAAA;AAGvD,MAAA,MAAM,YAAA,CAAa,MAAM,GAAG,CAAA;AAE5B,MAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,GAAA,EAAI;AAAA,IACtB,CAAA;AAAA,IAEA,MAAM,OAAA,CAEJ,GAAA,EACA,GAAA,EACe;AAEf,MAAA,IAAI,GAAA,CAAI,MAAA,CAAO,kBAAkB,CAAA,KAAM,gBAAA,EAAkB;AACvD,QAAA,MAAM,IAAIA,2BAAoB,iCAAiC,CAAA;AAAA,MACjE;AAEA,MAAA,IAAI;AACF,QAAA,MAAM,YAAA,GAAe,aAAA,CAAc,eAAA,CAAgB,GAAG,CAAA;AAGtD,QAAA,IAAI,CAAC,YAAA,EAAc;AACjB,UAAA,MAAM,IAAIT,kBAAW,wBAAwB,CAAA;AAAA,QAC/C;AAEA,QAAA,MAAM,YAAA,GAAe,MAAM,YAAA,CAAa,OAAA,CAAQ,GAAG,CAAA;AAEnD,QAAA,MAAM,MAAA,GAAS,MAAM,aAAA,CAAc,OAAA;AAAA,UACjC;AAAA,YACE,GAAA;AAAA,YACA,OAAO,YAAA,CAAa,KAAA;AAAA,YACpB,qBAAqB,YAAA,CAAa,mBAAA;AAAA,YAClC;AAAA,WACF;AAAA,UACA;AAAA,SACF;AAEA,QAAA,MAAM,YAAA,GAAe,MAAM,YAAA,CAAa,MAAA,CAAO,MAAM,CAAA;AAErD,QAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,MAAM,gBAAA,CAAiB,QAAQ,eAAe,CAAA;AAElE,QAAA,MAAM,eAAA,GAAkB,OAAO,OAAA,CAAQ,YAAA;AACvC,QAAA,IAAI,eAAA,IAAmB,oBAAoB,YAAA,EAAc;AACvD,UAAA,aAAA,CAAc,eAAA;AAAA,YACZ,GAAA;AAAA,YACA,eAAA;AAAA,YACA,GAAA,CAAI,IAAI,QAAQ;AAAA,WAClB;AAAA,QACF;AAEA,QAAA,MAAM,QAAA,GAAgC;AAAA,UACpC,OAAA;AAAA,UACA,YAAA,EAAc;AAAA,YACZ,OAAA,EAAS,OAAO,OAAA,CAAQ,OAAA;AAAA,YACxB,WAAA,EAAa,OAAO,OAAA,CAAQ,WAAA;AAAA,YAC5B,KAAA,EAAO,YAAA;AAAA,YACP,gBAAA,EAAkB,OAAO,OAAA,CAAQ;AAAA;AACnC,SACF;AAEA,QAAA,IAAI,cAAA,EAAgB;AAClB,UAAA,MAAM,WAAW,MAAM,cAAA;AAAA,YACrB,EAAE,SAAS,MAAA,EAAO;AAAA,YAClB;AAAA,WACF;AACA,UAAA,QAAA,CAAS,iBAAA,GACPM,kEAAiC,QAAQ,CAAA;AAAA,QAC7C;AAEA,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,QAAQ,CAAA;AAAA,MAC/B,SAAS,KAAA,EAAO;AACd,QAAA,MAAM,IAAIG,0BAAA,CAAoB,gBAAA,EAAkB,KAAK,CAAA;AAAA,MACvD;AAAA,IACF;AAAA,GACF;AACF;;;;"}
{"version":3,"file":"createOAuthRouteHandlers.cjs.js","sources":["../../src/oauth/createOAuthRouteHandlers.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 crypto from 'node:crypto';\nimport { URL } from 'node:url';\nimport {\n AuthenticationError,\n InputError,\n isError,\n NotAllowedError,\n} from '@backstage/errors';\nimport {\n encodeOAuthState,\n decodeOAuthState,\n OAuthStateTransform,\n} from './state';\nimport { sendWebMessageResponse } from '../flow';\nimport { prepareBackstageIdentityResponse } from '../identity';\nimport { OAuthCookieManager } from './OAuthCookieManager';\nimport {\n AuthProviderRouteHandlers,\n AuthResolverContext,\n ClientAuthResponse,\n CookieConfigurer,\n ProfileTransform,\n SignInResolver,\n} from '../types';\nimport { OAuthAuthenticator, OAuthAuthenticatorResult } from './types';\nimport { Config, readDurationFromConfig } from '@backstage/config';\nimport { CookieScopeManager } from './CookieScopeManager';\n\n/** @public */\nexport interface OAuthRouteHandlersOptions<TProfile> {\n authenticator: OAuthAuthenticator<any, TProfile>;\n appUrl: string;\n baseUrl: string;\n isOriginAllowed: (origin: string) => boolean;\n providerId: string;\n config: Config;\n resolverContext: AuthResolverContext;\n additionalScopes?: string[];\n stateTransform?: OAuthStateTransform;\n profileTransform?: ProfileTransform<OAuthAuthenticatorResult<TProfile>>;\n cookieConfigurer?: CookieConfigurer;\n signInResolver?: SignInResolver<OAuthAuthenticatorResult<TProfile>>;\n}\n\n/** @internal */\ntype ClientOAuthResponse = ClientAuthResponse<{\n /**\n * An access token issued for the signed in user.\n */\n accessToken: string;\n /**\n * (Optional) Id token issued for the signed in user.\n */\n idToken?: string;\n /**\n * Expiry of the access token in seconds.\n */\n expiresInSeconds?: number;\n /**\n * Scopes granted for the access token.\n */\n scope: string;\n}>;\n\n/** @public */\nexport function createOAuthRouteHandlers<TProfile>(\n options: OAuthRouteHandlersOptions<TProfile>,\n): AuthProviderRouteHandlers {\n const {\n authenticator,\n config,\n baseUrl,\n appUrl,\n providerId,\n isOriginAllowed,\n cookieConfigurer,\n resolverContext,\n signInResolver,\n } = options;\n\n const defaultAppOrigin = new URL(appUrl).origin;\n const callbackUrl =\n config.getOptionalString('callbackUrl') ??\n `${baseUrl}/${providerId}/handler/frame`;\n const sessionDuration = config.has('sessionDuration')\n ? readDurationFromConfig(config, { key: 'sessionDuration' })\n : undefined;\n\n const stateTransform = options.stateTransform ?? (state => ({ state }));\n const profileTransform =\n options.profileTransform ?? authenticator.defaultProfileTransform;\n const authenticatorCtx = authenticator.initialize({ config, callbackUrl });\n const cookieManager = new OAuthCookieManager({\n baseUrl,\n callbackUrl,\n defaultAppOrigin,\n providerId,\n cookieConfigurer,\n sessionDuration,\n });\n\n const scopeManager = CookieScopeManager.create({\n config,\n authenticator,\n cookieManager,\n additionalScopes: options.additionalScopes,\n });\n\n return {\n async start(\n this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const env = req.query.env?.toString();\n const origin = req.query.origin?.toString();\n const redirectUrl = req.query.redirectUrl?.toString();\n const flow = req.query.flow?.toString();\n\n if (!env) {\n throw new InputError('No env provided in request query parameters');\n }\n\n const nonce = crypto.randomBytes(16).toString('base64');\n // set a nonce cookie before redirecting to oauth provider\n cookieManager.setNonce(res, nonce, origin);\n\n const { scope, scopeState } = await scopeManager.start(req);\n\n const state = { nonce, env, origin, redirectUrl, flow, ...scopeState };\n const { state: transformedState } = await stateTransform(state, { req });\n\n const { url, status } = await options.authenticator.start(\n {\n req,\n scope,\n state: encodeOAuthState(transformedState),\n },\n authenticatorCtx,\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 this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n let origin = defaultAppOrigin;\n let state;\n\n try {\n state = decodeOAuthState(req.query.state?.toString() ?? '');\n\n if (state.origin) {\n try {\n origin = new URL(state.origin).origin;\n } catch {\n throw new NotAllowedError('App origin is invalid, failed to parse');\n }\n if (!isOriginAllowed(origin)) {\n throw new NotAllowedError(`Origin '${origin}' is not allowed`);\n }\n }\n\n // The same nonce is passed through cookie and state, and they must match\n const cookieNonce = cookieManager.getNonce(req);\n const stateNonce = state.nonce;\n if (!cookieNonce) {\n throw new NotAllowedError('Auth response is missing cookie nonce');\n }\n if (cookieNonce !== stateNonce) {\n throw new NotAllowedError('Invalid nonce');\n }\n\n const result = await authenticator.authenticate(\n { req },\n authenticatorCtx,\n );\n const { profile } = await profileTransform(result, resolverContext);\n\n const signInResult =\n signInResolver &&\n (await signInResolver({ profile, result }, resolverContext));\n\n const grantedScopes = await scopeManager.handleCallback(req, {\n result,\n state,\n origin,\n });\n\n const response: ClientOAuthResponse = {\n profile,\n providerInfo: {\n idToken: result.session.idToken,\n accessToken: result.session.accessToken,\n scope: grantedScopes,\n expiresInSeconds: result.session.expiresInSeconds,\n },\n ...(signInResult && {\n backstageIdentity: prepareBackstageIdentityResponse(signInResult),\n }),\n };\n\n if (result.session.refreshToken) {\n // set new refresh token\n cookieManager.setRefreshToken(\n res,\n result.session.refreshToken,\n origin,\n );\n }\n\n // When using the redirect flow we rely on refresh token we just\n // acquired to get a new session once we're back in the app.\n if (state.flow === 'redirect') {\n if (!state.redirectUrl) {\n throw new InputError(\n 'No redirectUrl provided in request query parameters',\n );\n }\n res.redirect(state.redirectUrl);\n return;\n }\n\n // post message back to popup if successful\n sendWebMessageResponse(res, origin, {\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\n if (state?.flow === 'redirect' && state?.redirectUrl) {\n const redirectUrl = new URL(state.redirectUrl);\n redirectUrl.searchParams.set('error', message);\n\n // set the error in a cookie and redirect user back to sign in where the error can be rendered\n res.redirect(redirectUrl.toString());\n } else {\n // post error message back to popup if failure\n sendWebMessageResponse(res, origin, {\n type: 'authorization_response',\n error: { name, message },\n });\n }\n }\n },\n\n async logout(\n this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n // We use this as a lightweight CSRF protection\n if (req.header('X-Requested-With') !== 'XMLHttpRequest') {\n throw new AuthenticationError('Invalid X-Requested-With header');\n }\n\n if (authenticator.logout) {\n const refreshToken = cookieManager.getRefreshToken(req);\n await authenticator.logout({ req, refreshToken }, authenticatorCtx);\n }\n\n // remove refresh token cookie if it is set\n cookieManager.removeRefreshToken(res, req.get('origin'));\n\n // remove persisted scopes\n await scopeManager.clear(req);\n\n res.status(200).end();\n },\n\n async refresh(\n this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n // We use this as a lightweight CSRF protection\n if (req.header('X-Requested-With') !== 'XMLHttpRequest') {\n throw new AuthenticationError('Invalid X-Requested-With header');\n }\n\n try {\n const refreshToken = cookieManager.getRefreshToken(req);\n\n // throw error if refresh token is missing in the request\n if (!refreshToken) {\n throw new InputError('Missing session cookie');\n }\n\n const scopeRefresh = await scopeManager.refresh(req);\n\n const result = await authenticator.refresh(\n {\n req,\n scope: scopeRefresh.scope,\n scopeAlreadyGranted: scopeRefresh.scopeAlreadyGranted,\n refreshToken,\n },\n authenticatorCtx,\n );\n\n const grantedScope = await scopeRefresh.commit(result);\n\n const { profile } = await profileTransform(result, resolverContext);\n\n const newRefreshToken = result.session.refreshToken;\n if (newRefreshToken && newRefreshToken !== refreshToken) {\n cookieManager.setRefreshToken(\n res,\n newRefreshToken,\n req.get('origin'),\n );\n }\n\n const response: ClientOAuthResponse = {\n profile,\n providerInfo: {\n idToken: result.session.idToken,\n accessToken: result.session.accessToken,\n scope: grantedScope,\n expiresInSeconds: result.session.expiresInSeconds,\n },\n };\n\n if (signInResolver) {\n const identity = await signInResolver(\n { profile, result },\n resolverContext,\n );\n response.backstageIdentity =\n prepareBackstageIdentityResponse(identity);\n }\n\n res.status(200).json(response);\n } catch (error) {\n throw new AuthenticationError('Refresh failed', error);\n }\n },\n };\n}\n"],"names":["config","URL","readDurationFromConfig","OAuthCookieManager","CookieScopeManager","InputError","crypto","state","encodeOAuthState","decodeOAuthState","NotAllowedError","prepareBackstageIdentityResponse","sendWebMessageResponse","isError","AuthenticationError"],"mappings":";;;;;;;;;;;;;;;;;AAkFO,SAAS,yBACd,OAAA,EAC2B;AAC3B,EAAA,MAAM;AAAA,IACJ,aAAA;AAAA,YACAA,QAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA,eAAA;AAAA,IACA,gBAAA;AAAA,IACA,eAAA;AAAA,IACA;AAAA,GACF,GAAI,OAAA;AAEJ,EAAA,MAAM,gBAAA,GAAmB,IAAIC,YAAA,CAAI,MAAM,CAAA,CAAE,MAAA;AACzC,EAAA,MAAM,WAAA,GACJD,SAAO,iBAAA,CAAkB,aAAa,KACtC,CAAA,EAAG,OAAO,IAAI,UAAU,CAAA,cAAA,CAAA;AAC1B,EAAA,MAAM,eAAA,GAAkBA,QAAA,CAAO,GAAA,CAAI,iBAAiB,CAAA,GAChDE,6BAAA,CAAuBF,QAAA,EAAQ,EAAE,GAAA,EAAK,iBAAA,EAAmB,CAAA,GACzD,MAAA;AAEJ,EAAA,MAAM,cAAA,GAAiB,OAAA,CAAQ,cAAA,KAAmB,CAAA,KAAA,MAAU,EAAE,KAAA,EAAM,CAAA,CAAA;AACpE,EAAA,MAAM,gBAAA,GACJ,OAAA,CAAQ,gBAAA,IAAoB,aAAA,CAAc,uBAAA;AAC5C,EAAA,MAAM,mBAAmB,aAAA,CAAc,UAAA,CAAW,UAAEA,QAAA,EAAQ,aAAa,CAAA;AACzE,EAAA,MAAM,aAAA,GAAgB,IAAIG,qCAAA,CAAmB;AAAA,IAC3C,OAAA;AAAA,IACA,WAAA;AAAA,IACA,gBAAA;AAAA,IACA,UAAA;AAAA,IACA,gBAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,MAAM,YAAA,GAAeC,sCAAmB,MAAA,CAAO;AAAA,YAC7CJ,QAAA;AAAA,IACA,aAAA;AAAA,IACA,aAAA;AAAA,IACA,kBAAkB,OAAA,CAAQ;AAAA,GAC3B,CAAA;AAED,EAAA,OAAO;AAAA,IACL,MAAM,KAAA,CAEJ,GAAA,EACA,GAAA,EACe;AACf,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,KAAA,CAAM,GAAA,EAAK,QAAA,EAAS;AACpC,MAAA,MAAM,MAAA,GAAS,GAAA,CAAI,KAAA,CAAM,MAAA,EAAQ,QAAA,EAAS;AAC1C,MAAA,MAAM,WAAA,GAAc,GAAA,CAAI,KAAA,CAAM,WAAA,EAAa,QAAA,EAAS;AACpD,MAAA,MAAM,IAAA,GAAO,GAAA,CAAI,KAAA,CAAM,IAAA,EAAM,QAAA,EAAS;AAEtC,MAAA,IAAI,CAAC,GAAA,EAAK;AACR,QAAA,MAAM,IAAIK,kBAAW,6CAA6C,CAAA;AAAA,MACpE;AAEA,MAAA,MAAM,QAAQC,uBAAA,CAAO,WAAA,CAAY,EAAE,CAAA,CAAE,SAAS,QAAQ,CAAA;AAEtD,MAAA,aAAA,CAAc,QAAA,CAAS,GAAA,EAAK,KAAA,EAAO,MAAM,CAAA;AAEzC,MAAA,MAAM,EAAE,KAAA,EAAO,UAAA,KAAe,MAAM,YAAA,CAAa,MAAM,GAAG,CAAA;AAE1D,MAAA,MAAMC,OAAA,GAAQ,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,WAAA,EAAa,IAAA,EAAM,GAAG,UAAA,EAAW;AACrE,MAAA,MAAM,EAAE,OAAO,gBAAA,EAAiB,GAAI,MAAM,cAAA,CAAeA,OAAA,EAAO,EAAE,GAAA,EAAK,CAAA;AAEvE,MAAA,MAAM,EAAE,GAAA,EAAK,MAAA,EAAO,GAAI,MAAM,QAAQ,aAAA,CAAc,KAAA;AAAA,QAClD;AAAA,UACE,GAAA;AAAA,UACA,KAAA;AAAA,UACA,KAAA,EAAOC,uBAAiB,gBAAgB;AAAA,SAC1C;AAAA,QACA;AAAA,OACF;AAEA,MAAA,GAAA,CAAI,aAAa,MAAA,IAAU,GAAA;AAC3B,MAAA,GAAA,CAAI,SAAA,CAAU,YAAY,GAAG,CAAA;AAC7B,MAAA,GAAA,CAAI,SAAA,CAAU,kBAAkB,GAAG,CAAA;AACnC,MAAA,GAAA,CAAI,GAAA,EAAI;AAAA,IACV,CAAA;AAAA,IAEA,MAAM,YAAA,CAEJ,GAAA,EACA,GAAA,EACe;AACf,MAAA,IAAI,MAAA,GAAS,gBAAA;AACb,MAAA,IAAID,OAAA;AAEJ,MAAA,IAAI;AACF,QAAAA,OAAA,GAAQE,uBAAiB,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,QAAA,MAAc,EAAE,CAAA;AAE1D,QAAA,IAAIF,QAAM,MAAA,EAAQ;AAChB,UAAA,IAAI;AACF,YAAA,MAAA,GAAS,IAAIN,YAAA,CAAIM,OAAA,CAAM,MAAM,CAAA,CAAE,MAAA;AAAA,UACjC,CAAA,CAAA,MAAQ;AACN,YAAA,MAAM,IAAIG,uBAAgB,wCAAwC,CAAA;AAAA,UACpE;AACA,UAAA,IAAI,CAAC,eAAA,CAAgB,MAAM,CAAA,EAAG;AAC5B,YAAA,MAAM,IAAIA,sBAAA,CAAgB,CAAA,QAAA,EAAW,MAAM,CAAA,gBAAA,CAAkB,CAAA;AAAA,UAC/D;AAAA,QACF;AAGA,QAAA,MAAM,WAAA,GAAc,aAAA,CAAc,QAAA,CAAS,GAAG,CAAA;AAC9C,QAAA,MAAM,aAAaH,OAAA,CAAM,KAAA;AACzB,QAAA,IAAI,CAAC,WAAA,EAAa;AAChB,UAAA,MAAM,IAAIG,uBAAgB,uCAAuC,CAAA;AAAA,QACnE;AACA,QAAA,IAAI,gBAAgB,UAAA,EAAY;AAC9B,UAAA,MAAM,IAAIA,uBAAgB,eAAe,CAAA;AAAA,QAC3C;AAEA,QAAA,MAAM,MAAA,GAAS,MAAM,aAAA,CAAc,YAAA;AAAA,UACjC,EAAE,GAAA,EAAI;AAAA,UACN;AAAA,SACF;AACA,QAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,MAAM,gBAAA,CAAiB,QAAQ,eAAe,CAAA;AAElE,QAAA,MAAM,YAAA,GACJ,kBACC,MAAM,cAAA,CAAe,EAAE,OAAA,EAAS,MAAA,IAAU,eAAe,CAAA;AAE5D,QAAA,MAAM,aAAA,GAAgB,MAAM,YAAA,CAAa,cAAA,CAAe,GAAA,EAAK;AAAA,UAC3D,MAAA;AAAA,iBACAH,OAAA;AAAA,UACA;AAAA,SACD,CAAA;AAED,QAAA,MAAM,QAAA,GAAgC;AAAA,UACpC,OAAA;AAAA,UACA,YAAA,EAAc;AAAA,YACZ,OAAA,EAAS,OAAO,OAAA,CAAQ,OAAA;AAAA,YACxB,WAAA,EAAa,OAAO,OAAA,CAAQ,WAAA;AAAA,YAC5B,KAAA,EAAO,aAAA;AAAA,YACP,gBAAA,EAAkB,OAAO,OAAA,CAAQ;AAAA,WACnC;AAAA,UACA,GAAI,YAAA,IAAgB;AAAA,YAClB,iBAAA,EAAmBI,kEAAiC,YAAY;AAAA;AAClE,SACF;AAEA,QAAA,IAAI,MAAA,CAAO,QAAQ,YAAA,EAAc;AAE/B,UAAA,aAAA,CAAc,eAAA;AAAA,YACZ,GAAA;AAAA,YACA,OAAO,OAAA,CAAQ,YAAA;AAAA,YACf;AAAA,WACF;AAAA,QACF;AAIA,QAAA,IAAIJ,OAAA,CAAM,SAAS,UAAA,EAAY;AAC7B,UAAA,IAAI,CAACA,QAAM,WAAA,EAAa;AACtB,YAAA,MAAM,IAAIF,iBAAA;AAAA,cACR;AAAA,aACF;AAAA,UACF;AACA,UAAA,GAAA,CAAI,QAAA,CAASE,QAAM,WAAW,CAAA;AAC9B,UAAA;AAAA,QACF;AAGA,QAAAK,6CAAA,CAAuB,KAAK,MAAA,EAAQ;AAAA,UAClC,IAAA,EAAM,wBAAA;AAAA,UACN;AAAA,SACD,CAAA;AAAA,MACH,SAAS,KAAA,EAAO;AACd,QAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAQ,GAAIC,cAAA,CAAQ,KAAK,CAAA,GACnC,KAAA,GACA,IAAI,KAAA,CAAM,2BAA2B,CAAA;AAEzC,QAAA,IAAIN,OAAA,EAAO,IAAA,KAAS,UAAA,IAAcA,OAAA,EAAO,WAAA,EAAa;AACpD,UAAA,MAAM,WAAA,GAAc,IAAIN,YAAA,CAAIM,OAAA,CAAM,WAAW,CAAA;AAC7C,UAAA,WAAA,CAAY,YAAA,CAAa,GAAA,CAAI,OAAA,EAAS,OAAO,CAAA;AAG7C,UAAA,GAAA,CAAI,QAAA,CAAS,WAAA,CAAY,QAAA,EAAU,CAAA;AAAA,QACrC,CAAA,MAAO;AAEL,UAAAK,6CAAA,CAAuB,KAAK,MAAA,EAAQ;AAAA,YAClC,IAAA,EAAM,wBAAA;AAAA,YACN,KAAA,EAAO,EAAE,IAAA,EAAM,OAAA;AAAQ,WACxB,CAAA;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAA;AAAA,IAEA,MAAM,MAAA,CAEJ,GAAA,EACA,GAAA,EACe;AAEf,MAAA,IAAI,GAAA,CAAI,MAAA,CAAO,kBAAkB,CAAA,KAAM,gBAAA,EAAkB;AACvD,QAAA,MAAM,IAAIE,2BAAoB,iCAAiC,CAAA;AAAA,MACjE;AAEA,MAAA,IAAI,cAAc,MAAA,EAAQ;AACxB,QAAA,MAAM,YAAA,GAAe,aAAA,CAAc,eAAA,CAAgB,GAAG,CAAA;AACtD,QAAA,MAAM,cAAc,MAAA,CAAO,EAAE,GAAA,EAAK,YAAA,IAAgB,gBAAgB,CAAA;AAAA,MACpE;AAGA,MAAA,aAAA,CAAc,kBAAA,CAAmB,GAAA,EAAK,GAAA,CAAI,GAAA,CAAI,QAAQ,CAAC,CAAA;AAGvD,MAAA,MAAM,YAAA,CAAa,MAAM,GAAG,CAAA;AAE5B,MAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,GAAA,EAAI;AAAA,IACtB,CAAA;AAAA,IAEA,MAAM,OAAA,CAEJ,GAAA,EACA,GAAA,EACe;AAEf,MAAA,IAAI,GAAA,CAAI,MAAA,CAAO,kBAAkB,CAAA,KAAM,gBAAA,EAAkB;AACvD,QAAA,MAAM,IAAIA,2BAAoB,iCAAiC,CAAA;AAAA,MACjE;AAEA,MAAA,IAAI;AACF,QAAA,MAAM,YAAA,GAAe,aAAA,CAAc,eAAA,CAAgB,GAAG,CAAA;AAGtD,QAAA,IAAI,CAAC,YAAA,EAAc;AACjB,UAAA,MAAM,IAAIT,kBAAW,wBAAwB,CAAA;AAAA,QAC/C;AAEA,QAAA,MAAM,YAAA,GAAe,MAAM,YAAA,CAAa,OAAA,CAAQ,GAAG,CAAA;AAEnD,QAAA,MAAM,MAAA,GAAS,MAAM,aAAA,CAAc,OAAA;AAAA,UACjC;AAAA,YACE,GAAA;AAAA,YACA,OAAO,YAAA,CAAa,KAAA;AAAA,YACpB,qBAAqB,YAAA,CAAa,mBAAA;AAAA,YAClC;AAAA,WACF;AAAA,UACA;AAAA,SACF;AAEA,QAAA,MAAM,YAAA,GAAe,MAAM,YAAA,CAAa,MAAA,CAAO,MAAM,CAAA;AAErD,QAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,MAAM,gBAAA,CAAiB,QAAQ,eAAe,CAAA;AAElE,QAAA,MAAM,eAAA,GAAkB,OAAO,OAAA,CAAQ,YAAA;AACvC,QAAA,IAAI,eAAA,IAAmB,oBAAoB,YAAA,EAAc;AACvD,UAAA,aAAA,CAAc,eAAA;AAAA,YACZ,GAAA;AAAA,YACA,eAAA;AAAA,YACA,GAAA,CAAI,IAAI,QAAQ;AAAA,WAClB;AAAA,QACF;AAEA,QAAA,MAAM,QAAA,GAAgC;AAAA,UACpC,OAAA;AAAA,UACA,YAAA,EAAc;AAAA,YACZ,OAAA,EAAS,OAAO,OAAA,CAAQ,OAAA;AAAA,YACxB,WAAA,EAAa,OAAO,OAAA,CAAQ,WAAA;AAAA,YAC5B,KAAA,EAAO,YAAA;AAAA,YACP,gBAAA,EAAkB,OAAO,OAAA,CAAQ;AAAA;AACnC,SACF;AAEA,QAAA,IAAI,cAAA,EAAgB;AAClB,UAAA,MAAM,WAAW,MAAM,cAAA;AAAA,YACrB,EAAE,SAAS,MAAA,EAAO;AAAA,YAClB;AAAA,WACF;AACA,UAAA,QAAA,CAAS,iBAAA,GACPM,kEAAiC,QAAQ,CAAA;AAAA,QAC7C;AAEA,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,QAAQ,CAAA;AAAA,MAC/B,SAAS,KAAA,EAAO;AACd,QAAA,MAAM,IAAIG,0BAAA,CAAoB,gBAAA,EAAkB,KAAK,CAAA;AAAA,MACvD;AAAA,IACF;AAAA,GACF;AACF;;;;"}
{
"name": "@backstage/plugin-auth-node",
"version": "0.0.0-nightly-20260127025640",
"version": "0.0.0-nightly-20260128025246",
"backstage": {

@@ -41,3 +41,3 @@ "role": "node-library",

"dependencies": {
"@backstage/backend-plugin-api": "0.0.0-nightly-20260127025640",
"@backstage/backend-plugin-api": "0.0.0-nightly-20260128025246",
"@backstage/catalog-client": "1.12.1",

@@ -59,5 +59,5 @@ "@backstage/catalog-model": "1.7.6",

"devDependencies": {
"@backstage/backend-defaults": "0.0.0-nightly-20260127025640",
"@backstage/backend-test-utils": "0.0.0-nightly-20260127025640",
"@backstage/cli": "0.0.0-nightly-20260127025640",
"@backstage/backend-defaults": "0.0.0-nightly-20260128025246",
"@backstage/backend-test-utils": "0.0.0-nightly-20260128025246",
"@backstage/cli": "0.0.0-nightly-20260128025246",
"cookie-parser": "^1.4.6",

@@ -64,0 +64,0 @@ "express-promise-router": "^4.1.1",