@backstage/plugin-auth-node 0.7.7-next.0 → 0.7.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,24 @@
1
1
  # @backstage/plugin-auth-node
2
2
 
3
+ ## 0.7.7
4
+
5
+ ### Patch Changes
6
+
7
+ - 9724edd: Hardened return URL validation in the OAuth redirect flow. Failed sign-in attempts now return to the configured app URL with the error message, including when the return URL is invalid.
8
+ - Updated dependencies
9
+ - @backstage/backend-plugin-api@1.11.0
10
+ - @backstage/catalog-client@1.16.3
11
+ - @backstage/config@1.3.10
12
+ - @backstage/errors@1.3.2
13
+ - @backstage/catalog-model@1.10.2
14
+
15
+ ## 0.7.7-next.1
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies
20
+ - @backstage/backend-plugin-api@1.11.0-next.1
21
+
3
22
  ## 0.7.7-next.0
4
23
 
5
24
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -641,8 +641,10 @@ declare function createOAuthAuthenticator<TContext, TProfile>(authenticator: OAu
641
641
  /** @public */
642
642
  interface OAuthRouteHandlersOptions<TProfile> {
643
643
  authenticator: OAuthAuthenticator<any, TProfile>;
644
+ /** App URL to return to when the redirect flow fails. */
644
645
  appUrl: string;
645
646
  baseUrl: string;
647
+ /** Checks allowed app origins for web messages and redirect destinations. */
646
648
  isOriginAllowed: (origin: string) => boolean;
647
649
  providerId: string;
648
650
  config: Config;
@@ -47,6 +47,23 @@ function createOAuthRouteHandlers(options) {
47
47
  cookieManager,
48
48
  additionalScopes: options.additionalScopes
49
49
  });
50
+ function parseRedirectUrl(value) {
51
+ if (!value) {
52
+ throw new errors.InputError(
53
+ "No redirectUrl provided in request query parameters"
54
+ );
55
+ }
56
+ let url;
57
+ try {
58
+ url = new node_url.URL(value);
59
+ } catch {
60
+ throw new errors.InputError("Redirect URL is invalid, failed to parse");
61
+ }
62
+ if (!["http:", "https:"].includes(url.protocol) || !isOriginAllowed(url.origin)) {
63
+ throw new errors.NotAllowedError(`Redirect URL is not an allowed origin`);
64
+ }
65
+ return url;
66
+ }
50
67
  return {
51
68
  async start(req, res) {
52
69
  const env = req.query.env?.toString();
@@ -63,6 +80,9 @@ function createOAuthRouteHandlers(options) {
63
80
  }
64
81
  const redirectUrl = req.query.redirectUrl?.toString();
65
82
  const flow = req.query.flow?.toString();
83
+ if (flow === "redirect") {
84
+ parseRedirectUrl(redirectUrl);
85
+ }
66
86
  if (!env) {
67
87
  throw new errors.InputError("No env provided in request query parameters");
68
88
  }
@@ -87,8 +107,12 @@ function createOAuthRouteHandlers(options) {
87
107
  async frameHandler(req, res) {
88
108
  let origin = defaultAppOrigin;
89
109
  let state$1;
110
+ let redirectUrl;
90
111
  try {
91
112
  state$1 = state.decodeOAuthState(req.query.state?.toString() ?? "");
113
+ if (state$1.flow === "redirect") {
114
+ redirectUrl = parseRedirectUrl(state$1.redirectUrl);
115
+ }
92
116
  if (state$1.origin) {
93
117
  try {
94
118
  origin = new node_url.URL(state$1.origin).origin;
@@ -137,13 +161,8 @@ function createOAuthRouteHandlers(options) {
137
161
  origin
138
162
  );
139
163
  }
140
- if (state$1.flow === "redirect") {
141
- if (!state$1.redirectUrl) {
142
- throw new errors.InputError(
143
- "No redirectUrl provided in request query parameters"
144
- );
145
- }
146
- res.redirect(state$1.redirectUrl);
164
+ if (redirectUrl) {
165
+ res.redirect(redirectUrl.toString());
147
166
  return;
148
167
  }
149
168
  sendWebMessageResponse.sendWebMessageResponse(res, origin, {
@@ -152,10 +171,11 @@ function createOAuthRouteHandlers(options) {
152
171
  });
153
172
  } catch (error) {
154
173
  const { name, message } = errors.isError(error) ? error : new Error("Encountered invalid error");
155
- if (state$1?.flow === "redirect" && state$1?.redirectUrl) {
156
- const redirectUrl = new node_url.URL(state$1.redirectUrl);
157
- redirectUrl.searchParams.set("error", message);
158
- res.redirect(redirectUrl.toString());
174
+ if (state$1?.flow === "redirect") {
175
+ const errorUrl = new node_url.URL(appUrl);
176
+ errorUrl.search = new URLSearchParams({ error: message }).toString();
177
+ errorUrl.hash = "";
178
+ res.redirect(errorUrl.toString());
159
179
  } else {
160
180
  sendWebMessageResponse.sendWebMessageResponse(res, origin, {
161
181
  type: "authorization_response",
@@ -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 '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 {\n OAuthAuthenticator,\n OAuthAuthenticatorLogoutResult,\n OAuthAuthenticatorResult,\n} 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 let origin = req.query.origin?.toString();\n if (origin !== undefined) {\n try {\n origin = new URL(origin).origin;\n } catch {\n throw new InputError('App origin is invalid, failed to parse');\n }\n\n if (!isOriginAllowed(origin)) {\n throw new NotAllowedError(`Origin '${origin}' is not allowed`);\n }\n }\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 const origin = req.get('origin');\n if (origin && !isOriginAllowed(origin)) {\n throw new NotAllowedError(`Origin '${origin}' is not allowed`);\n }\n\n let logoutResult: void | OAuthAuthenticatorLogoutResult = undefined;\n if (authenticator.logout) {\n const refreshToken = cookieManager.getRefreshToken(req);\n logoutResult = await authenticator.logout(\n { req, refreshToken },\n authenticatorCtx,\n );\n }\n\n // remove refresh token cookie if it is set\n cookieManager.removeRefreshToken(res, origin);\n\n // remove persisted scopes\n await scopeManager.clear(req);\n\n if (logoutResult?.logoutUrl) {\n try {\n const logoutUrl = new URL(logoutResult.logoutUrl);\n if (\n logoutUrl.protocol === 'https:' ||\n logoutUrl.hostname === 'localhost'\n ) {\n res.status(200).json({ logoutUrl: logoutResult.logoutUrl });\n return;\n }\n } catch {\n // Malformed URL — fall through to empty response\n }\n }\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","NotAllowedError","crypto","state","encodeOAuthState","decodeOAuthState","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 {\n OAuthAuthenticator,\n OAuthAuthenticatorLogoutResult,\n OAuthAuthenticatorResult,\n} 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 /** App URL to return to when the redirect flow fails. */\n appUrl: string;\n baseUrl: string;\n /** Checks allowed app origins for web messages and redirect destinations. */\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 function parseRedirectUrl(value: string | undefined): URL {\n if (!value) {\n throw new InputError(\n 'No redirectUrl provided in request query parameters',\n );\n }\n\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new InputError('Redirect URL is invalid, failed to parse');\n }\n\n if (\n !['http:', 'https:'].includes(url.protocol) ||\n !isOriginAllowed(url.origin)\n ) {\n throw new NotAllowedError(`Redirect URL is not an allowed origin`);\n }\n\n return url;\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 let origin = req.query.origin?.toString();\n if (origin !== undefined) {\n try {\n origin = new URL(origin).origin;\n } catch {\n throw new InputError('App origin is invalid, failed to parse');\n }\n\n if (!isOriginAllowed(origin)) {\n throw new NotAllowedError(`Origin '${origin}' is not allowed`);\n }\n }\n const redirectUrl = req.query.redirectUrl?.toString();\n const flow = req.query.flow?.toString();\n\n if (flow === 'redirect') {\n parseRedirectUrl(redirectUrl);\n }\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 let redirectUrl: URL | undefined;\n\n try {\n state = decodeOAuthState(req.query.state?.toString() ?? '');\n\n if (state.flow === 'redirect') {\n redirectUrl = parseRedirectUrl(state.redirectUrl);\n }\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 (redirectUrl) {\n res.redirect(redirectUrl.toString());\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') {\n const errorUrl = new URL(appUrl);\n errorUrl.search = new URLSearchParams({ error: message }).toString();\n errorUrl.hash = '';\n\n // Redirect back to sign in where the error can be rendered.\n res.redirect(errorUrl.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 const origin = req.get('origin');\n if (origin && !isOriginAllowed(origin)) {\n throw new NotAllowedError(`Origin '${origin}' is not allowed`);\n }\n\n let logoutResult: void | OAuthAuthenticatorLogoutResult = undefined;\n if (authenticator.logout) {\n const refreshToken = cookieManager.getRefreshToken(req);\n logoutResult = await authenticator.logout(\n { req, refreshToken },\n authenticatorCtx,\n );\n }\n\n // remove refresh token cookie if it is set\n cookieManager.removeRefreshToken(res, origin);\n\n // remove persisted scopes\n await scopeManager.clear(req);\n\n if (logoutResult?.logoutUrl) {\n try {\n const logoutUrl = new URL(logoutResult.logoutUrl);\n if (\n logoutUrl.protocol === 'https:' ||\n logoutUrl.hostname === 'localhost'\n ) {\n res.status(200).json({ logoutUrl: logoutResult.logoutUrl });\n return;\n }\n } catch {\n // Malformed URL — fall through to empty response\n }\n }\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","NotAllowedError","crypto","state","encodeOAuthState","decodeOAuthState","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.7.7-next.0",
3
+ "version": "0.7.7",
4
4
  "backstage": {
5
5
  "role": "node-library",
6
6
  "pluginId": "auth",
@@ -38,12 +38,12 @@
38
38
  "test": "backstage-cli package test"
39
39
  },
40
40
  "dependencies": {
41
- "@backstage/backend-plugin-api": "1.10.2-next.0",
42
- "@backstage/catalog-client": "1.16.3-next.0",
43
- "@backstage/catalog-model": "1.10.2-next.0",
44
- "@backstage/config": "1.3.10-next.0",
45
- "@backstage/errors": "1.3.2-next.0",
46
- "@backstage/types": "1.2.2",
41
+ "@backstage/backend-plugin-api": "^1.11.0",
42
+ "@backstage/catalog-client": "^1.16.3",
43
+ "@backstage/catalog-model": "^1.10.2",
44
+ "@backstage/config": "^1.3.10",
45
+ "@backstage/errors": "^1.3.2",
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",
@@ -55,9 +55,9 @@
55
55
  "zod-validation-error": "^5.0.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@backstage/backend-defaults": "0.18.1-next.0",
59
- "@backstage/backend-test-utils": "1.11.8-next.0",
60
- "@backstage/cli": "0.36.7-next.0",
58
+ "@backstage/backend-defaults": "^0.18.1",
59
+ "@backstage/backend-test-utils": "^1.11.8",
60
+ "@backstage/cli": "^0.36.7",
61
61
  "cookie-parser": "^1.4.6",
62
62
  "express-promise-router": "^4.1.1",
63
63
  "msw": "^2.0.0",