@backstage/plugin-auth-node 0.6.11 → 0.6.12-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @backstage/plugin-auth-node
2
2
 
3
+ ## 0.6.12-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 7455dae: Use node prefix on native imports
8
+ - 69d880e: Bump to latest zod to ensure it has the latest features
9
+ - Updated dependencies
10
+ - @backstage/backend-plugin-api@1.7.0-next.0
11
+ - @backstage/catalog-client@1.12.1
12
+ - @backstage/catalog-model@1.7.6
13
+ - @backstage/config@1.3.6
14
+ - @backstage/errors@1.2.7
15
+ - @backstage/types@1.2.2
16
+
3
17
  ## 0.6.11
4
18
 
5
19
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var crypto = require('crypto');
3
+ var crypto = require('node:crypto');
4
4
  var errors = require('@backstage/errors');
5
5
 
6
6
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
@@ -1 +1 @@
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;;;;;"}
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 '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;;;;;"}
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var crypto = require('crypto');
4
- var url = require('url');
3
+ var crypto = require('node:crypto');
4
+ var node_url = require('node:url');
5
5
  var errors = require('@backstage/errors');
6
6
  var state = require('./state.cjs.js');
7
7
  var sendWebMessageResponse = require('../flow/sendWebMessageResponse.cjs.js');
@@ -27,7 +27,7 @@ function createOAuthRouteHandlers(options) {
27
27
  resolverContext,
28
28
  signInResolver
29
29
  } = options;
30
- const defaultAppOrigin = new url.URL(appUrl).origin;
30
+ const defaultAppOrigin = new node_url.URL(appUrl).origin;
31
31
  const callbackUrl = config$1.getOptionalString("callbackUrl") ?? `${baseUrl}/${providerId}/handler/frame`;
32
32
  const sessionDuration = config$1.has("sessionDuration") ? config.readDurationFromConfig(config$1, { key: "sessionDuration" }) : void 0;
33
33
  const stateTransform = options.stateTransform ?? ((state) => ({ state }));
@@ -81,7 +81,7 @@ function createOAuthRouteHandlers(options) {
81
81
  state$1 = state.decodeOAuthState(req.query.state?.toString() ?? "");
82
82
  if (state$1.origin) {
83
83
  try {
84
- origin = new url.URL(state$1.origin).origin;
84
+ origin = new node_url.URL(state$1.origin).origin;
85
85
  } catch {
86
86
  throw new errors.NotAllowedError("App origin is invalid, failed to parse");
87
87
  }
@@ -143,7 +143,7 @@ function createOAuthRouteHandlers(options) {
143
143
  } catch (error) {
144
144
  const { name, message } = errors.isError(error) ? error : new Error("Encountered invalid error");
145
145
  if (state$1?.flow === "redirect" && state$1?.redirectUrl) {
146
- const redirectUrl = new url.URL(state$1.redirectUrl);
146
+ const redirectUrl = new node_url.URL(state$1.redirectUrl);
147
147
  redirectUrl.searchParams.set("error", message);
148
148
  res.redirect(redirectUrl.toString());
149
149
  } else {
@@ -1 +1 @@
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":"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"}
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 '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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-auth-node",
3
- "version": "0.6.11",
3
+ "version": "0.6.12-next.0",
4
4
  "backstage": {
5
5
  "role": "node-library",
6
6
  "pluginId": "auth",
@@ -38,26 +38,26 @@
38
38
  "test": "backstage-cli package test"
39
39
  },
40
40
  "dependencies": {
41
- "@backstage/backend-plugin-api": "^1.6.1",
42
- "@backstage/catalog-client": "^1.12.1",
43
- "@backstage/catalog-model": "^1.7.6",
44
- "@backstage/config": "^1.3.6",
45
- "@backstage/errors": "^1.2.7",
46
- "@backstage/types": "^1.2.2",
41
+ "@backstage/backend-plugin-api": "1.7.0-next.0",
42
+ "@backstage/catalog-client": "1.12.1",
43
+ "@backstage/catalog-model": "1.7.6",
44
+ "@backstage/config": "1.3.6",
45
+ "@backstage/errors": "1.2.7",
46
+ "@backstage/types": "1.2.2",
47
47
  "@types/express": "^4.17.6",
48
48
  "@types/passport": "^1.0.3",
49
49
  "express": "^4.22.0",
50
50
  "jose": "^5.0.0",
51
51
  "lodash": "^4.17.21",
52
52
  "passport": "^0.7.0",
53
- "zod": "^3.22.4",
53
+ "zod": "^3.25.76",
54
54
  "zod-to-json-schema": "^3.25.1",
55
55
  "zod-validation-error": "^4.0.2"
56
56
  },
57
57
  "devDependencies": {
58
- "@backstage/backend-defaults": "^0.15.0",
59
- "@backstage/backend-test-utils": "^1.10.3",
60
- "@backstage/cli": "^0.35.2",
58
+ "@backstage/backend-defaults": "0.15.1-next.0",
59
+ "@backstage/backend-test-utils": "1.10.4-next.0",
60
+ "@backstage/cli": "0.35.3-next.0",
61
61
  "cookie-parser": "^1.4.6",
62
62
  "express-promise-router": "^4.1.1",
63
63
  "lodash": "^4.17.21",