From 3c1df5d4a957caa1e67ed837a3f00b7029ebdfa2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 29 Jul 2023 11:19:31 +0200 Subject: [PATCH] auth-node: createOAuthHandleres -> createOAuthRouteHandlers + refactor state transform Signed-off-by: Patrik Oldsberg --- ...andlers.ts => createOAuthRouteHandlers.ts} | 26 +++++++++++-------- plugins/auth-node/src/oauth/index.ts | 6 ++--- plugins/auth-node/src/oauth/state.ts | 21 ++++++--------- 3 files changed, 26 insertions(+), 27 deletions(-) rename plugins/auth-node/src/oauth/{createOAuthHandlers.ts => createOAuthRouteHandlers.ts} (93%) diff --git a/plugins/auth-node/src/oauth/createOAuthHandlers.ts b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.ts similarity index 93% rename from plugins/auth-node/src/oauth/createOAuthHandlers.ts rename to plugins/auth-node/src/oauth/createOAuthRouteHandlers.ts index e4fd3e5a50..fa6cb57e31 100644 --- a/plugins/auth-node/src/oauth/createOAuthHandlers.ts +++ b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.ts @@ -23,7 +23,12 @@ import { isError, NotAllowedError, } from '@backstage/errors'; -import { defaultStateDecoder, defaultStateEncoder, OAuthState } from './state'; +import { + encodeOAuthState, + decodeOAuthState, + OAuthStateTransform, + OAuthState, +} from './state'; import { sendWebMessageResponse } from '../flow'; import { prepareBackstageIdentityResponse } from '../identity'; import { OAuthCookieManager } from './OAuthCookieManager'; @@ -42,15 +47,15 @@ import { import { Config } from '@backstage/config'; /** @public */ -export interface OAuthHandlersOptions { - authenticator: OAuthAuthenticator; +export interface OAuthRouteHandlersOptions { + authenticator: OAuthAuthenticator; appUrl: string; baseUrl: string; isOriginAllowed: (origin: string) => boolean; - callbackUrl: string; providerId: string; config: Config; resolverContext: AuthResolverContext; + stateTransform?: OAuthStateTransform; profileTransform?: OAuthProfileTransform; cookieConfigurer?: CookieConfigurer; signInResolver?: SignInResolver>; @@ -77,8 +82,8 @@ type ClientOAuthResponse = ClientAuthResponse<{ }>; /** @public */ -export function createOAuthHandlers( - options: OAuthHandlersOptions, +export function createOAuthRouteHandlers( + options: OAuthRouteHandlersOptions, ): AuthProviderRouteHandlers { const { authenticator, @@ -97,6 +102,7 @@ export function createOAuthHandlers( config.getOptionalString('callbackUrl') ?? `${baseUrl}/${providerId}/handler/frame`; + const stateTransform = options.stateTransform ?? (state => ({ state })); const profileTransform = options.profileTransform ?? authenticator.defaultProfileTransform; const authenticatorCtx = authenticator.initialize({ config, callbackUrl }); @@ -137,7 +143,8 @@ export function createOAuthHandlers( state.scope = scope; } - const { encodedState } = await defaultStateEncoder(state, { req }); + const { state: transformedState } = await stateTransform(state, { req }); + const encodedState = encodeOAuthState(transformedState); const { url, status } = await options.authenticator.start( { req, scope, state: encodedState }, @@ -158,10 +165,7 @@ export function createOAuthHandlers( let appOrigin = defaultAppOrigin; try { - const { state } = await defaultStateDecoder( - req.query.state?.toString() ?? '', - { req }, - ); + const state = decodeOAuthState(req.query.state?.toString() ?? ''); if (state.origin) { try { diff --git a/plugins/auth-node/src/oauth/index.ts b/plugins/auth-node/src/oauth/index.ts index a2de5f4a27..e07ee01a58 100644 --- a/plugins/auth-node/src/oauth/index.ts +++ b/plugins/auth-node/src/oauth/index.ts @@ -15,9 +15,9 @@ */ export { - createOAuthHandlers, - type OAuthHandlersOptions, -} from './createOAuthHandlers'; + createOAuthRouteHandlers, + type OAuthRouteHandlersOptions, +} from './createOAuthRouteHandlers'; export { PassportOAuthAuthenticatorHelper, type PassportOAuthDoneCallback, diff --git a/plugins/auth-node/src/oauth/state.ts b/plugins/auth-node/src/oauth/state.ts index 668fe3736a..71c5a2afb8 100644 --- a/plugins/auth-node/src/oauth/state.ts +++ b/plugins/auth-node/src/oauth/state.ts @@ -17,6 +17,7 @@ import pickBy from 'lodash/pickBy'; import { Request } from 'express'; +/** @public */ export type OAuthState = { /* A type for the serialized value in the `state` parameter of the OAuth authorization flow */ @@ -29,28 +30,22 @@ export type OAuthState = { }; /** @public */ -export type OAuthStateEncoder = ( +export type OAuthStateTransform = ( state: OAuthState, context: { req: Request }, -) => Promise<{ encodedState: string }>; - -/** @public */ -export type OAuthStateDecoder = ( - encodedState: string, - context: { req: Request }, ) => Promise<{ state: OAuthState }>; /** @public */ -export const defaultStateEncoder: OAuthStateEncoder = async state => { +export function encodeOAuthState(state: OAuthState): string { const stateString = new URLSearchParams( pickBy(state, value => value !== undefined), ).toString(); - return { encodedState: Buffer.from(stateString, 'utf-8').toString('hex') }; -}; + return Buffer.from(stateString, 'utf-8').toString('hex'); +} /** @public */ -export const defaultStateDecoder: OAuthStateDecoder = async encodedState => { +export function decodeOAuthState(encodedState: string): OAuthState { const state = Object.fromEntries( new URLSearchParams(Buffer.from(encodedState, 'hex').toString('utf-8')), ); @@ -63,5 +58,5 @@ export const defaultStateDecoder: OAuthStateDecoder = async encodedState => { throw Error(`Invalid state passed via request`); } - return { state: state as OAuthState }; -}; + return state as OAuthState; +}