From 01d4bd510999f638725dea7506c38f3c3e7be3fd Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 8 Apr 2022 14:37:55 +0200 Subject: [PATCH] auth-backend: migrate all other providers to use integration helper Signed-off-by: Patrik Oldsberg --- .../src/providers/bitbucket/provider.ts | 101 +++++----- .../src/providers/gcp-iap/provider.ts | 72 ++++---- .../src/providers/github/provider.ts | 173 ++++++++++-------- .../src/providers/gitlab/provider.ts | 107 ++++++----- .../src/providers/microsoft/provider.ts | 111 ++++++----- .../src/providers/oauth2-proxy/provider.ts | 36 ++-- .../src/providers/oauth2/provider.ts | 102 ++++++----- .../src/providers/oidc/provider.ts | 109 ++++++----- .../src/providers/okta/provider.ts | 115 ++++++------ .../src/providers/onelogin/provider.ts | 104 ++++++----- .../src/providers/saml/provider.ts | 110 ++++++----- 11 files changed, 637 insertions(+), 503 deletions(-) diff --git a/plugins/auth-backend/src/providers/bitbucket/provider.ts b/plugins/auth-backend/src/providers/bitbucket/provider.ts index c59e755130..7a3e6c9a60 100644 --- a/plugins/auth-backend/src/providers/bitbucket/provider.ts +++ b/plugins/auth-backend/src/providers/bitbucket/provider.ts @@ -36,8 +36,8 @@ import { makeProfileInfo, PassportDoneCallback, } from '../../lib/passport'; +import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; import { - AuthProviderFactory, AuthHandler, RedirectInfo, SignInResolver, @@ -245,52 +245,65 @@ export type BitbucketProviderOptions = { }; }; -export const createBitbucketProvider = (options?: { - /** - * The profile transformation function used to verify and convert the auth response - * into the profile that will be presented to the user. - */ - authHandler?: AuthHandler; - - /** - * Configure sign-in for this provider, without it the provider can not be used to sign users in. - */ - signIn?: { +/** + * Auth provider integration for BitBucket auth + * + * @public + */ +export const bitbucket = createAuthProviderIntegration({ + create(options?: { /** - * Maps an auth result to a Backstage identity for the user. + * The profile transformation function used to verify and convert the auth response + * into the profile that will be presented to the user. */ - resolver: SignInResolver; - }; -}): AuthProviderFactory => { - return ({ providerId, globalConfig, config, resolverContext }) => - OAuthEnvironmentHandler.mapConfig(config, envConfig => { - const clientId = envConfig.getString('clientId'); - const clientSecret = envConfig.getString('clientSecret'); - const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); - const callbackUrl = - customCallbackUrl || - `${globalConfig.baseUrl}/${providerId}/handler/frame`; + authHandler?: AuthHandler; - const authHandler: AuthHandler = - options?.authHandler - ? options.authHandler - : async ({ fullProfile, params }) => ({ - profile: makeProfileInfo(fullProfile, params.id_token), - }); + /** + * Configure sign-in for this provider, without it the provider can not be used to sign users in. + */ + signIn?: { + /** + * Maps an auth result to a Backstage identity for the user. + */ + resolver: SignInResolver; + }; + }) { + return ({ providerId, globalConfig, config, resolverContext }) => + OAuthEnvironmentHandler.mapConfig(config, envConfig => { + const clientId = envConfig.getString('clientId'); + const clientSecret = envConfig.getString('clientSecret'); + const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); + const callbackUrl = + customCallbackUrl || + `${globalConfig.baseUrl}/${providerId}/handler/frame`; - const provider = new BitbucketAuthProvider({ - clientId, - clientSecret, - callbackUrl, - signInResolver: options?.signIn?.resolver, - authHandler, - resolverContext, + const authHandler: AuthHandler = + options?.authHandler + ? options.authHandler + : async ({ fullProfile, params }) => ({ + profile: makeProfileInfo(fullProfile, params.id_token), + }); + + const provider = new BitbucketAuthProvider({ + clientId, + clientSecret, + callbackUrl, + signInResolver: options?.signIn?.resolver, + authHandler, + resolverContext, + }); + + return OAuthAdapter.fromConfig(globalConfig, provider, { + disableRefresh: false, + providerId, + callbackUrl, + }); }); + }, +}); - return OAuthAdapter.fromConfig(globalConfig, provider, { - disableRefresh: false, - providerId, - callbackUrl, - }); - }); -}; +/** + * @public + * @deprecated Use `providers.bitbucket.create` instead + */ +export const createBitbucketProvider = bitbucket.create; diff --git a/plugins/auth-backend/src/providers/gcp-iap/provider.ts b/plugins/auth-backend/src/providers/gcp-iap/provider.ts index e1fe5a4f01..cb5d5979cd 100644 --- a/plugins/auth-backend/src/providers/gcp-iap/provider.ts +++ b/plugins/auth-backend/src/providers/gcp-iap/provider.ts @@ -16,10 +16,10 @@ import express from 'express'; import { TokenPayload } from 'google-auth-library'; +import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; import { prepareBackstageIdentityResponse } from '../prepareBackstageIdentityResponse'; import { AuthHandler, - AuthProviderFactory, AuthProviderRouteHandlers, AuthResolverContext, SignInResolver, @@ -77,41 +77,49 @@ export class GcpIapProvider implements AuthProviderRouteHandlers { } /** - * Creates an auth provider for Google Identity-Aware Proxy. + * Auth provider integration for Google Identity-Aware Proxy auth * * @public */ -export function createGcpIapProvider(options: { - /** - * The profile transformation function used to verify and convert the auth - * response into the profile that will be presented to the user. The default - * implementation just provides the authenticated email that the IAP - * presented. - */ - authHandler?: AuthHandler; - - /** - * Configures sign-in for this provider. - */ - signIn: { +export const gcpIap = createAuthProviderIntegration({ + create(options: { /** - * Maps an auth result to a Backstage identity for the user. + * The profile transformation function used to verify and convert the auth + * response into the profile that will be presented to the user. The default + * implementation just provides the authenticated email that the IAP + * presented. */ - resolver: SignInResolver; - }; -}): AuthProviderFactory { - return ({ config, resolverContext }) => { - const audience = config.getString('audience'); + authHandler?: AuthHandler; - const authHandler = options.authHandler ?? defaultAuthHandler; - const signInResolver = options.signIn.resolver; - const tokenValidator = createTokenValidator(audience); + /** + * Configures sign-in for this provider. + */ + signIn: { + /** + * Maps an auth result to a Backstage identity for the user. + */ + resolver: SignInResolver; + }; + }) { + return ({ config, resolverContext }) => { + const audience = config.getString('audience'); - return new GcpIapProvider({ - authHandler, - signInResolver, - tokenValidator, - resolverContext, - }); - }; -} + const authHandler = options.authHandler ?? defaultAuthHandler; + const signInResolver = options.signIn.resolver; + const tokenValidator = createTokenValidator(audience); + + return new GcpIapProvider({ + authHandler, + signInResolver, + tokenValidator, + resolverContext, + }); + }; + }, +}); + +/** + * @public + * @deprecated Use `providers.gcpIap.create` instead + */ +export const createGcpIapProvider = gcpIap.create; diff --git a/plugins/auth-backend/src/providers/github/provider.ts b/plugins/auth-backend/src/providers/github/provider.ts index 770715d0ae..d84c89261f 100644 --- a/plugins/auth-backend/src/providers/github/provider.ts +++ b/plugins/auth-backend/src/providers/github/provider.ts @@ -27,7 +27,6 @@ import { } from '../../lib/passport'; import { RedirectInfo, - AuthProviderFactory, AuthHandler, SignInResolver, StateEncoder, @@ -42,6 +41,7 @@ import { encodeState, OAuthRefreshRequest, } from '../../lib/oauth'; +import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; const ACCESS_TOKEN_PREFIX = 'access-token.'; @@ -279,91 +279,106 @@ export type GithubProviderOptions = { stateEncoder?: StateEncoder; }; -export const createGithubProvider = (options?: { - /** - * The profile transformation function used to verify and convert the auth response - * into the profile that will be presented to the user. - */ - authHandler?: AuthHandler; - - /** - * Configure sign-in for this provider, without it the provider can not be used to sign users in. - */ - signIn?: { +/** + * Auth provider integration for GitHub auth + * + * @public + */ +export const github = createAuthProviderIntegration({ + create(options?: { /** - * Maps an auth result to a Backstage identity for the user. + * The profile transformation function used to verify and convert the auth response + * into the profile that will be presented to the user. */ - resolver: SignInResolver; - }; + authHandler?: AuthHandler; - /** - * The state encoder used to encode the 'state' parameter on the OAuth request. - * - * It should return a string that takes the state params (from the request), url encodes the params - * and finally base64 encodes them. - * - * Providing your own stateEncoder will allow you to add addition parameters to the state field. - * - * It is typed as follows: - * `export type StateEncoder = (input: OAuthState) => Promise<{encodedState: string}>;` - * - * Note: the stateEncoder must encode a 'nonce' value and an 'env' value. Without this, the OAuth flow will fail - * (These two values will be set by the req.state by default) - * - * For more information, please see the helper module in ../../oauth/helpers #readState - */ - stateEncoder?: StateEncoder; -}): AuthProviderFactory => { - return ({ providerId, globalConfig, config, resolverContext }) => - OAuthEnvironmentHandler.mapConfig(config, envConfig => { - const clientId = envConfig.getString('clientId'); - const clientSecret = envConfig.getString('clientSecret'); - const enterpriseInstanceUrl = envConfig - .getOptionalString('enterpriseInstanceUrl') - ?.replace(/\/$/, ''); - const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); - const authorizationUrl = enterpriseInstanceUrl - ? `${enterpriseInstanceUrl}/login/oauth/authorize` - : undefined; - const tokenUrl = enterpriseInstanceUrl - ? `${enterpriseInstanceUrl}/login/oauth/access_token` - : undefined; - const userProfileUrl = enterpriseInstanceUrl - ? `${enterpriseInstanceUrl}/api/v3/user` - : undefined; - const callbackUrl = - customCallbackUrl || - `${globalConfig.baseUrl}/${providerId}/handler/frame`; + /** + * Configure sign-in for this provider, without it the provider can not be used to sign users in. + */ + signIn?: { + /** + * Maps an auth result to a Backstage identity for the user. + */ + resolver: SignInResolver; + }; - const authHandler: AuthHandler = options?.authHandler - ? options.authHandler - : async ({ fullProfile }) => ({ - profile: makeProfileInfo(fullProfile), + /** + * The state encoder used to encode the 'state' parameter on the OAuth request. + * + * It should return a string that takes the state params (from the request), url encodes the params + * and finally base64 encodes them. + * + * Providing your own stateEncoder will allow you to add addition parameters to the state field. + * + * It is typed as follows: + * `export type StateEncoder = (input: OAuthState) => Promise<{encodedState: string}>;` + * + * Note: the stateEncoder must encode a 'nonce' value and an 'env' value. Without this, the OAuth flow will fail + * (These two values will be set by the req.state by default) + * + * For more information, please see the helper module in ../../oauth/helpers #readState + */ + stateEncoder?: StateEncoder; + }) { + return ({ providerId, globalConfig, config, resolverContext }) => + OAuthEnvironmentHandler.mapConfig(config, envConfig => { + const clientId = envConfig.getString('clientId'); + const clientSecret = envConfig.getString('clientSecret'); + const enterpriseInstanceUrl = envConfig + .getOptionalString('enterpriseInstanceUrl') + ?.replace(/\/$/, ''); + const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); + const authorizationUrl = enterpriseInstanceUrl + ? `${enterpriseInstanceUrl}/login/oauth/authorize` + : undefined; + const tokenUrl = enterpriseInstanceUrl + ? `${enterpriseInstanceUrl}/login/oauth/access_token` + : undefined; + const userProfileUrl = enterpriseInstanceUrl + ? `${enterpriseInstanceUrl}/api/v3/user` + : undefined; + const callbackUrl = + customCallbackUrl || + `${globalConfig.baseUrl}/${providerId}/handler/frame`; + + const authHandler: AuthHandler = options?.authHandler + ? options.authHandler + : async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }); + + const stateEncoder: StateEncoder = + options?.stateEncoder ?? + (async ( + req: OAuthStartRequest, + ): Promise<{ encodedState: string }> => { + return { encodedState: encodeState(req.state) }; }); - const stateEncoder: StateEncoder = - options?.stateEncoder ?? - (async (req: OAuthStartRequest): Promise<{ encodedState: string }> => { - return { encodedState: encodeState(req.state) }; + const provider = new GithubAuthProvider({ + clientId, + clientSecret, + callbackUrl, + tokenUrl, + userProfileUrl, + authorizationUrl, + signInResolver: options?.signIn?.resolver, + authHandler, + stateEncoder, + resolverContext, }); - const provider = new GithubAuthProvider({ - clientId, - clientSecret, - callbackUrl, - tokenUrl, - userProfileUrl, - authorizationUrl, - signInResolver: options?.signIn?.resolver, - authHandler, - stateEncoder, - resolverContext, + return OAuthAdapter.fromConfig(globalConfig, provider, { + persistScopes: true, + providerId, + callbackUrl, + }); }); + }, +}); - return OAuthAdapter.fromConfig(globalConfig, provider, { - persistScopes: true, - providerId, - callbackUrl, - }); - }); -}; +/** + * @public + * @deprecated Use `providers.github.create` instead + */ +export const createGithubProvider = github.create; diff --git a/plugins/auth-backend/src/providers/gitlab/provider.ts b/plugins/auth-backend/src/providers/gitlab/provider.ts index 292ed3efa5..b8de3845a5 100644 --- a/plugins/auth-backend/src/providers/gitlab/provider.ts +++ b/plugins/auth-backend/src/providers/gitlab/provider.ts @@ -26,7 +26,6 @@ import { } from '../../lib/passport'; import { RedirectInfo, - AuthProviderFactory, SignInResolver, AuthHandler, AuthResolverContext, @@ -42,6 +41,7 @@ import { encodeState, OAuthResult, } from '../../lib/oauth'; +import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; type PrivateInfo = { refreshToken: string; @@ -202,54 +202,67 @@ export type GitlabProviderOptions = { }; }; -export const createGitlabProvider = (options?: { - /** - * The profile transformation function used to verify and convert the auth response - * into the profile that will be presented to the user. - */ - authHandler?: AuthHandler; +/** + * Auth provider integration for GitLab auth + * + * @public + */ +export const gitlab = createAuthProviderIntegration({ + create(options?: { + /** + * The profile transformation function used to verify and convert the auth response + * into the profile that will be presented to the user. + */ + authHandler?: AuthHandler; - /** - * Configure sign-in for this provider, without it the provider can not be used to sign users in. - */ - /** - * Maps an auth result to a Backstage identity for the user. - * - * Set to `'email'` to use the default email-based sign in resolver, which will search - * the catalog for a single user entity that has a matching `microsoft.com/email` annotation. - */ - signIn?: { - resolver: SignInResolver; - }; -}): AuthProviderFactory => { - return ({ providerId, globalConfig, config, resolverContext }) => - OAuthEnvironmentHandler.mapConfig(config, envConfig => { - const clientId = envConfig.getString('clientId'); - const clientSecret = envConfig.getString('clientSecret'); - const audience = envConfig.getOptionalString('audience'); - const baseUrl = audience || 'https://gitlab.com'; - const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); - const callbackUrl = - customCallbackUrl || - `${globalConfig.baseUrl}/${providerId}/handler/frame`; + /** + * Configure sign-in for this provider, without it the provider can not be used to sign users in. + */ + /** + * Maps an auth result to a Backstage identity for the user. + * + * Set to `'email'` to use the default email-based sign in resolver, which will search + * the catalog for a single user entity that has a matching `microsoft.com/email` annotation. + */ + signIn?: { + resolver: SignInResolver; + }; + }) { + return ({ providerId, globalConfig, config, resolverContext }) => + OAuthEnvironmentHandler.mapConfig(config, envConfig => { + const clientId = envConfig.getString('clientId'); + const clientSecret = envConfig.getString('clientSecret'); + const audience = envConfig.getOptionalString('audience'); + const baseUrl = audience || 'https://gitlab.com'; + const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); + const callbackUrl = + customCallbackUrl || + `${globalConfig.baseUrl}/${providerId}/handler/frame`; - const authHandler: AuthHandler = - options?.authHandler ?? gitlabDefaultAuthHandler; + const authHandler: AuthHandler = + options?.authHandler ?? gitlabDefaultAuthHandler; - const provider = new GitlabAuthProvider({ - clientId, - clientSecret, - callbackUrl, - baseUrl, - authHandler, - signInResolver: options?.signIn?.resolver, - resolverContext, + const provider = new GitlabAuthProvider({ + clientId, + clientSecret, + callbackUrl, + baseUrl, + authHandler, + signInResolver: options?.signIn?.resolver, + resolverContext, + }); + + return OAuthAdapter.fromConfig(globalConfig, provider, { + disableRefresh: false, + providerId, + callbackUrl, + }); }); + }, +}); - return OAuthAdapter.fromConfig(globalConfig, provider, { - disableRefresh: false, - providerId, - callbackUrl, - }); - }); -}; +/** + * @public + * @deprecated Use `providers.gitlab.create` instead + */ +export const createGitlabProvider = gitlab.create; diff --git a/plugins/auth-backend/src/providers/microsoft/provider.ts b/plugins/auth-backend/src/providers/microsoft/provider.ts index 053ab64547..106ab66b6d 100644 --- a/plugins/auth-backend/src/providers/microsoft/provider.ts +++ b/plugins/auth-backend/src/providers/microsoft/provider.ts @@ -37,12 +37,12 @@ import { PassportDoneCallback, } from '../../lib/passport'; import { - AuthProviderFactory, AuthHandler, RedirectInfo, SignInResolver, AuthResolverContext, } from '../types'; +import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; import { Logger } from 'winston'; import fetch from 'node-fetch'; @@ -224,58 +224,71 @@ export type MicrosoftProviderOptions = { }; }; -export const createMicrosoftProvider = (options?: { - /** - * The profile transformation function used to verify and convert the auth response - * into the profile that will be presented to the user. - */ - authHandler?: AuthHandler; - - /** - * Configure sign-in for this provider, without it the provider can not be used to sign users in. - */ - signIn?: { +/** + * Auth provider integration for Microsoft auth + * + * @public + */ +export const microsoft = createAuthProviderIntegration({ + create(options?: { /** - * Maps an auth result to a Backstage identity for the user. + * The profile transformation function used to verify and convert the auth response + * into the profile that will be presented to the user. */ - resolver: SignInResolver; - }; -}): AuthProviderFactory => { - return ({ providerId, globalConfig, config, logger, resolverContext }) => - OAuthEnvironmentHandler.mapConfig(config, envConfig => { - const clientId = envConfig.getString('clientId'); - const clientSecret = envConfig.getString('clientSecret'); - const tenantId = envConfig.getString('tenantId'); + authHandler?: AuthHandler; - const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); - const callbackUrl = - customCallbackUrl || - `${globalConfig.baseUrl}/${providerId}/handler/frame`; - const authorizationUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`; - const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`; + /** + * Configure sign-in for this provider, without it the provider can not be used to sign users in. + */ + signIn?: { + /** + * Maps an auth result to a Backstage identity for the user. + */ + resolver: SignInResolver; + }; + }) { + return ({ providerId, globalConfig, config, logger, resolverContext }) => + OAuthEnvironmentHandler.mapConfig(config, envConfig => { + const clientId = envConfig.getString('clientId'); + const clientSecret = envConfig.getString('clientSecret'); + const tenantId = envConfig.getString('tenantId'); - const authHandler: AuthHandler = options?.authHandler - ? options.authHandler - : async ({ fullProfile, params }) => ({ - profile: makeProfileInfo(fullProfile, params.id_token), - }); + const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); + const callbackUrl = + customCallbackUrl || + `${globalConfig.baseUrl}/${providerId}/handler/frame`; + const authorizationUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`; + const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`; - const provider = new MicrosoftAuthProvider({ - clientId, - clientSecret, - callbackUrl, - authorizationUrl, - tokenUrl, - authHandler, - signInResolver: options?.signIn?.resolver, - logger, - resolverContext, + const authHandler: AuthHandler = options?.authHandler + ? options.authHandler + : async ({ fullProfile, params }) => ({ + profile: makeProfileInfo(fullProfile, params.id_token), + }); + + const provider = new MicrosoftAuthProvider({ + clientId, + clientSecret, + callbackUrl, + authorizationUrl, + tokenUrl, + authHandler, + signInResolver: options?.signIn?.resolver, + logger, + resolverContext, + }); + + return OAuthAdapter.fromConfig(globalConfig, provider, { + disableRefresh: false, + providerId, + callbackUrl, + }); }); + }, +}); - return OAuthAdapter.fromConfig(globalConfig, provider, { - disableRefresh: false, - providerId, - callbackUrl, - }); - }); -}; +/** + * @public + * @deprecated Use `providers.microsoft.create` instead + */ +export const createMicrosoftProvider = microsoft.create; diff --git a/plugins/auth-backend/src/providers/oauth2-proxy/provider.ts b/plugins/auth-backend/src/providers/oauth2-proxy/provider.ts index ed3e5bce81..a7a8befd5e 100644 --- a/plugins/auth-backend/src/providers/oauth2-proxy/provider.ts +++ b/plugins/auth-backend/src/providers/oauth2-proxy/provider.ts @@ -20,13 +20,13 @@ import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-no import { AuthHandler, SignInResolver, - AuthProviderFactory, AuthProviderRouteHandlers, AuthResponse, AuthResolverContext, } from '../types'; import { JWT } from 'jose'; import { prepareBackstageIdentityResponse } from '../prepareBackstageIdentityResponse'; +import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; export const OAUTH2_PROXY_JWT_HEADER = 'X-OAUTH2-PROXY-ID-TOKEN'; @@ -151,12 +151,12 @@ export class Oauth2ProxyAuthProvider } /** - * Factory function for oauth2-proxy auth provider + * Auth provider integration for oauth2-proxy auth * * @public */ -export const createOauth2ProxyProvider = - (options: { +export const oauth2Proxy = createAuthProviderIntegration({ + create(options: { /** * Configure an auth handler to generate a profile for the user. */ @@ -171,13 +171,21 @@ export const createOauth2ProxyProvider = */ resolver: SignInResolver>; }; - }): AuthProviderFactory => - ({ resolverContext }) => { - const signInResolver = options.signIn.resolver; - const authHandler = options.authHandler; - return new Oauth2ProxyAuthProvider({ - resolverContext, - signInResolver, - authHandler, - }); - }; + }) { + return ({ resolverContext }) => { + const signInResolver = options.signIn.resolver; + const authHandler = options.authHandler; + return new Oauth2ProxyAuthProvider({ + resolverContext, + signInResolver, + authHandler, + }); + }; + }, +}); + +/** + * @public + * @deprecated Use `providers.oauth2Proxy.create` instead + */ +export const createOauth2ProxyProvider = oauth2Proxy.create; diff --git a/plugins/auth-backend/src/providers/oauth2/provider.ts b/plugins/auth-backend/src/providers/oauth2/provider.ts index 312efd6d1f..7af10a522f 100644 --- a/plugins/auth-backend/src/providers/oauth2/provider.ts +++ b/plugins/auth-backend/src/providers/oauth2/provider.ts @@ -38,11 +38,11 @@ import { } from '../../lib/passport'; import { AuthHandler, - AuthProviderFactory, AuthResolverContext, RedirectInfo, SignInResolver, } from '../types'; +import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; type PrivateInfo = { refreshToken: string; @@ -196,51 +196,65 @@ export type OAuth2ProviderOptions = { }; }; -export const createOAuth2Provider = (options?: { - authHandler?: AuthHandler; +/** + * Auth provider integration for generic OAuth2 auth + * + * @public + */ +export const oauth2 = createAuthProviderIntegration({ + create(options?: { + authHandler?: AuthHandler; - signIn?: { - resolver: SignInResolver; - }; -}): AuthProviderFactory => { - return ({ providerId, globalConfig, config, resolverContext }) => - OAuthEnvironmentHandler.mapConfig(config, envConfig => { - const clientId = envConfig.getString('clientId'); - const clientSecret = envConfig.getString('clientSecret'); - const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); - const callbackUrl = - customCallbackUrl || - `${globalConfig.baseUrl}/${providerId}/handler/frame`; - const authorizationUrl = envConfig.getString('authorizationUrl'); - const tokenUrl = envConfig.getString('tokenUrl'); - const scope = envConfig.getOptionalString('scope'); - const includeBasicAuth = envConfig.getOptionalBoolean('includeBasicAuth'); - const disableRefresh = - envConfig.getOptionalBoolean('disableRefresh') ?? false; + signIn?: { + resolver: SignInResolver; + }; + }) { + return ({ providerId, globalConfig, config, resolverContext }) => + OAuthEnvironmentHandler.mapConfig(config, envConfig => { + const clientId = envConfig.getString('clientId'); + const clientSecret = envConfig.getString('clientSecret'); + const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); + const callbackUrl = + customCallbackUrl || + `${globalConfig.baseUrl}/${providerId}/handler/frame`; + const authorizationUrl = envConfig.getString('authorizationUrl'); + const tokenUrl = envConfig.getString('tokenUrl'); + const scope = envConfig.getOptionalString('scope'); + const includeBasicAuth = + envConfig.getOptionalBoolean('includeBasicAuth'); + const disableRefresh = + envConfig.getOptionalBoolean('disableRefresh') ?? false; - const authHandler: AuthHandler = options?.authHandler - ? options.authHandler - : async ({ fullProfile, params }) => ({ - profile: makeProfileInfo(fullProfile, params.id_token), - }); + const authHandler: AuthHandler = options?.authHandler + ? options.authHandler + : async ({ fullProfile, params }) => ({ + profile: makeProfileInfo(fullProfile, params.id_token), + }); - const provider = new OAuth2AuthProvider({ - clientId, - clientSecret, - callbackUrl, - signInResolver: options?.signIn?.resolver, - authHandler, - authorizationUrl, - tokenUrl, - scope, - includeBasicAuth, - resolverContext, + const provider = new OAuth2AuthProvider({ + clientId, + clientSecret, + callbackUrl, + signInResolver: options?.signIn?.resolver, + authHandler, + authorizationUrl, + tokenUrl, + scope, + includeBasicAuth, + resolverContext, + }); + + return OAuthAdapter.fromConfig(globalConfig, provider, { + disableRefresh, + providerId, + callbackUrl, + }); }); + }, +}); - return OAuthAdapter.fromConfig(globalConfig, provider, { - disableRefresh, - providerId, - callbackUrl, - }); - }); -}; +/** + * @public + * @deprecated Use `providers.oauth2.create` instead + */ +export const createOAuth2Provider = oauth2.create; diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index 53729ce7cc..71f58df354 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -39,11 +39,11 @@ import { } from '../../lib/passport'; import { AuthHandler, - AuthProviderFactory, AuthResolverContext, RedirectInfo, SignInResolver, } from '../types'; +import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; type PrivateInfo = { refreshToken?: string; @@ -209,55 +209,68 @@ export type OidcProviderOptions = { }; }; -export const createOidcProvider = (options?: { - authHandler?: AuthHandler; +/** + * Auth provider integration for generic OpenID Connect auth + * + * @public + */ +export const oidc = createAuthProviderIntegration({ + create(options?: { + authHandler?: AuthHandler; - signIn?: { - resolver: SignInResolver; - }; -}): AuthProviderFactory => { - return ({ providerId, globalConfig, config, resolverContext }) => - OAuthEnvironmentHandler.mapConfig(config, envConfig => { - const clientId = envConfig.getString('clientId'); - const clientSecret = envConfig.getString('clientSecret'); - const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); - const callbackUrl = - customCallbackUrl || - `${globalConfig.baseUrl}/${providerId}/handler/frame`; - const metadataUrl = envConfig.getString('metadataUrl'); - const tokenSignedResponseAlg = envConfig.getOptionalString( - 'tokenSignedResponseAlg', - ); - const scope = envConfig.getOptionalString('scope'); - const prompt = envConfig.getOptionalString('prompt'); + signIn?: { + resolver: SignInResolver; + }; + }) { + return ({ providerId, globalConfig, config, resolverContext }) => + OAuthEnvironmentHandler.mapConfig(config, envConfig => { + const clientId = envConfig.getString('clientId'); + const clientSecret = envConfig.getString('clientSecret'); + const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); + const callbackUrl = + customCallbackUrl || + `${globalConfig.baseUrl}/${providerId}/handler/frame`; + const metadataUrl = envConfig.getString('metadataUrl'); + const tokenSignedResponseAlg = envConfig.getOptionalString( + 'tokenSignedResponseAlg', + ); + const scope = envConfig.getOptionalString('scope'); + const prompt = envConfig.getOptionalString('prompt'); - const authHandler: AuthHandler = options?.authHandler - ? options.authHandler - : async ({ userinfo }) => ({ - profile: { - displayName: userinfo.name, - email: userinfo.email, - picture: userinfo.picture, - }, - }); + const authHandler: AuthHandler = options?.authHandler + ? options.authHandler + : async ({ userinfo }) => ({ + profile: { + displayName: userinfo.name, + email: userinfo.email, + picture: userinfo.picture, + }, + }); - const provider = new OidcAuthProvider({ - clientId, - clientSecret, - callbackUrl, - tokenSignedResponseAlg, - metadataUrl, - scope, - prompt, - signInResolver: options?.signIn?.resolver, - authHandler, - resolverContext, + const provider = new OidcAuthProvider({ + clientId, + clientSecret, + callbackUrl, + tokenSignedResponseAlg, + metadataUrl, + scope, + prompt, + signInResolver: options?.signIn?.resolver, + authHandler, + resolverContext, + }); + + return OAuthAdapter.fromConfig(globalConfig, provider, { + disableRefresh: false, + providerId, + callbackUrl, + }); }); + }, +}); - return OAuthAdapter.fromConfig(globalConfig, provider, { - disableRefresh: false, - providerId, - callbackUrl, - }); - }); -}; +/** + * @public + * @deprecated Use `providers.oidc.create` instead + */ +export const createOidcProvider = oidc.create; diff --git a/plugins/auth-backend/src/providers/okta/provider.ts b/plugins/auth-backend/src/providers/okta/provider.ts index 606a9bbce7..bbfa4d05d8 100644 --- a/plugins/auth-backend/src/providers/okta/provider.ts +++ b/plugins/auth-backend/src/providers/okta/provider.ts @@ -37,12 +37,12 @@ import { PassportDoneCallback, } from '../../lib/passport'; import { - AuthProviderFactory, AuthHandler, RedirectInfo, SignInResolver, AuthResolverContext, } from '../types'; +import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; import { StateStore } from 'passport-oauth2'; type PrivateInfo = { @@ -226,60 +226,73 @@ export type OktaProviderOptions = { }; }; -export const createOktaProvider = (_options?: { - /** - * The profile transformation function used to verify and convert the auth response - * into the profile that will be presented to the user. - */ - authHandler?: AuthHandler; - - /** - * Configure sign-in for this provider, without it the provider can not be used to sign users in. - */ - signIn?: { +/** + * Auth provider integration for Okta auth + * + * @public + */ +export const okta = createAuthProviderIntegration({ + create(options?: { /** - * Maps an auth result to a Backstage identity for the user. + * The profile transformation function used to verify and convert the auth response + * into the profile that will be presented to the user. */ - resolver: SignInResolver; - }; -}): AuthProviderFactory => { - return ({ providerId, globalConfig, config, resolverContext }) => - OAuthEnvironmentHandler.mapConfig(config, envConfig => { - const clientId = envConfig.getString('clientId'); - const clientSecret = envConfig.getString('clientSecret'); - const audience = envConfig.getString('audience'); - const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); - const callbackUrl = - customCallbackUrl || - `${globalConfig.baseUrl}/${providerId}/handler/frame`; + authHandler?: AuthHandler; - // This is a safe assumption as `passport-okta-oauth` uses the audience - // as the base for building the authorization, token, and user info URLs. - // https://github.com/fischerdan/passport-okta-oauth/blob/ea9ac42d/lib/passport-okta-oauth/oauth2.js#L12-L14 - if (!audience.startsWith('https://')) { - throw new Error("URL for 'audience' must start with 'https://'."); - } + /** + * Configure sign-in for this provider, without it the provider can not be used to sign users in. + */ + signIn?: { + /** + * Maps an auth result to a Backstage identity for the user. + */ + resolver: SignInResolver; + }; + }) { + return ({ providerId, globalConfig, config, resolverContext }) => + OAuthEnvironmentHandler.mapConfig(config, envConfig => { + const clientId = envConfig.getString('clientId'); + const clientSecret = envConfig.getString('clientSecret'); + const audience = envConfig.getString('audience'); + const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); + const callbackUrl = + customCallbackUrl || + `${globalConfig.baseUrl}/${providerId}/handler/frame`; - const authHandler: AuthHandler = _options?.authHandler - ? _options.authHandler - : async ({ fullProfile, params }) => ({ - profile: makeProfileInfo(fullProfile, params.id_token), - }); + // This is a safe assumption as `passport-okta-oauth` uses the audience + // as the base for building the authorization, token, and user info URLs. + // https://github.com/fischerdan/passport-okta-oauth/blob/ea9ac42d/lib/passport-okta-oauth/oauth2.js#L12-L14 + if (!audience.startsWith('https://')) { + throw new Error("URL for 'audience' must start with 'https://'."); + } - const provider = new OktaAuthProvider({ - audience, - clientId, - clientSecret, - callbackUrl, - authHandler, - signInResolver: _options?.signIn?.resolver, - resolverContext, + const authHandler: AuthHandler = options?.authHandler + ? options.authHandler + : async ({ fullProfile, params }) => ({ + profile: makeProfileInfo(fullProfile, params.id_token), + }); + + const provider = new OktaAuthProvider({ + audience, + clientId, + clientSecret, + callbackUrl, + authHandler, + signInResolver: options?.signIn?.resolver, + resolverContext, + }); + + return OAuthAdapter.fromConfig(globalConfig, provider, { + disableRefresh: false, + providerId, + callbackUrl, + }); }); + }, +}); - return OAuthAdapter.fromConfig(globalConfig, provider, { - disableRefresh: false, - providerId, - callbackUrl, - }); - }); -}; +/** + * @public + * @deprecated Use `providers.okta.create` instead + */ +export const createOktaProvider = okta.create; diff --git a/plugins/auth-backend/src/providers/onelogin/provider.ts b/plugins/auth-backend/src/providers/onelogin/provider.ts index 05699fcb17..09e3eeeddb 100644 --- a/plugins/auth-backend/src/providers/onelogin/provider.ts +++ b/plugins/auth-backend/src/providers/onelogin/provider.ts @@ -38,11 +38,11 @@ import { } from '../../lib/passport'; import { RedirectInfo, - AuthProviderFactory, AuthHandler, SignInResolver, AuthResolverContext, } from '../types'; +import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; type PrivateInfo = { refreshToken: string; @@ -187,54 +187,66 @@ export type OneLoginProviderOptions = { }; }; -/** @public */ -export const createOneLoginProvider = (options?: { - /** - * The profile transformation function used to verify and convert the auth response - * into the profile that will be presented to the user. - */ - authHandler?: AuthHandler; - - /** - * Configure sign-in for this provider, without it the provider can not be used to sign users in. - */ - signIn?: { +/** + * Auth provider integration for OneLogin auth + * + * @public + */ +export const onelogin = createAuthProviderIntegration({ + create(options?: { /** - * Maps an auth result to a Backstage identity for the user. + * The profile transformation function used to verify and convert the auth response + * into the profile that will be presented to the user. */ - resolver: SignInResolver; - }; -}): AuthProviderFactory => { - return ({ providerId, globalConfig, config, resolverContext }) => - OAuthEnvironmentHandler.mapConfig(config, envConfig => { - const clientId = envConfig.getString('clientId'); - const clientSecret = envConfig.getString('clientSecret'); - const issuer = envConfig.getString('issuer'); - const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); - const callbackUrl = - customCallbackUrl || - `${globalConfig.baseUrl}/${providerId}/handler/frame`; + authHandler?: AuthHandler; - const authHandler: AuthHandler = options?.authHandler - ? options.authHandler - : async ({ fullProfile, params }) => ({ - profile: makeProfileInfo(fullProfile, params.id_token), - }); + /** + * Configure sign-in for this provider, without it the provider can not be used to sign users in. + */ + signIn?: { + /** + * Maps an auth result to a Backstage identity for the user. + */ + resolver: SignInResolver; + }; + }) { + return ({ providerId, globalConfig, config, resolverContext }) => + OAuthEnvironmentHandler.mapConfig(config, envConfig => { + const clientId = envConfig.getString('clientId'); + const clientSecret = envConfig.getString('clientSecret'); + const issuer = envConfig.getString('issuer'); + const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); + const callbackUrl = + customCallbackUrl || + `${globalConfig.baseUrl}/${providerId}/handler/frame`; - const provider = new OneLoginProvider({ - clientId, - clientSecret, - callbackUrl, - issuer, - authHandler, - signInResolver: options?.signIn?.resolver, - resolverContext, + const authHandler: AuthHandler = options?.authHandler + ? options.authHandler + : async ({ fullProfile, params }) => ({ + profile: makeProfileInfo(fullProfile, params.id_token), + }); + + const provider = new OneLoginProvider({ + clientId, + clientSecret, + callbackUrl, + issuer, + authHandler, + signInResolver: options?.signIn?.resolver, + resolverContext, + }); + + return OAuthAdapter.fromConfig(globalConfig, provider, { + disableRefresh: false, + providerId, + callbackUrl, + }); }); + }, +}); - return OAuthAdapter.fromConfig(globalConfig, provider, { - disableRefresh: false, - providerId, - callbackUrl, - }); - }); -}; +/** + * @public + * @deprecated Use `providers.onelogin.create` instead + */ +export const createOneLoginProvider = onelogin.create; diff --git a/plugins/auth-backend/src/providers/saml/provider.ts b/plugins/auth-backend/src/providers/saml/provider.ts index 592510acf5..3009b6bedf 100644 --- a/plugins/auth-backend/src/providers/saml/provider.ts +++ b/plugins/auth-backend/src/providers/saml/provider.ts @@ -28,13 +28,13 @@ import { } from '../../lib/passport'; import { AuthProviderRouteHandlers, - AuthProviderFactory, AuthHandler, SignInResolver, AuthResponse, AuthResolverContext, } from '../types'; import { postMessageResponse } from '../../lib/flow'; +import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; import { AuthenticationError, isError } from '@backstage/errors'; import { prepareBackstageIdentityResponse } from '../prepareBackstageIdentityResponse'; @@ -167,55 +167,67 @@ export type SamlProviderOptions = { }; }; -/** @public */ -export const createSamlProvider = (options?: { - /** - * The profile transformation function used to verify and convert the auth response - * into the profile that will be presented to the user. - */ - authHandler?: AuthHandler; - - /** - * Configure sign-in for this provider, without it the provider can not be used to sign users in. - */ - signIn?: { +/** + * Auth provider integration for SAML auth + * + * @public + */ +export const saml = createAuthProviderIntegration({ + create(options?: { /** - * Maps an auth result to a Backstage identity for the user. + * The profile transformation function used to verify and convert the auth response + * into the profile that will be presented to the user. */ - resolver: SignInResolver; - }; -}): AuthProviderFactory => { - return ({ providerId, globalConfig, config, resolverContext }) => { - const authHandler: AuthHandler = options?.authHandler - ? options.authHandler - : async ({ fullProfile }) => ({ - profile: { - email: fullProfile.email, - displayName: fullProfile.displayName, - }, - }); + authHandler?: AuthHandler; - return new SamlAuthProvider({ - callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`, - entryPoint: config.getString('entryPoint'), - logoutUrl: config.getOptionalString('logoutUrl'), - audience: config.getOptionalString('audience'), - issuer: config.getString('issuer'), - cert: config.getString('cert'), - privateKey: config.getOptionalString('privateKey'), - authnContext: config.getOptionalStringArray('authnContext'), - identifierFormat: config.getOptionalString('identifierFormat'), - decryptionPvk: config.getOptionalString('decryptionPvk'), - signatureAlgorithm: config.getOptionalString('signatureAlgorithm') as - | SignatureAlgorithm - | undefined, - digestAlgorithm: config.getOptionalString('digestAlgorithm'), - acceptedClockSkewMs: config.getOptionalNumber('acceptedClockSkewMs'), + /** + * Configure sign-in for this provider, without it the provider can not be used to sign users in. + */ + signIn?: { + /** + * Maps an auth result to a Backstage identity for the user. + */ + resolver: SignInResolver; + }; + }) { + return ({ providerId, globalConfig, config, resolverContext }) => { + const authHandler: AuthHandler = options?.authHandler + ? options.authHandler + : async ({ fullProfile }) => ({ + profile: { + email: fullProfile.email, + displayName: fullProfile.displayName, + }, + }); - appUrl: globalConfig.appUrl, - authHandler, - signInResolver: options?.signIn?.resolver, - resolverContext, - }); - }; -}; + return new SamlAuthProvider({ + callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`, + entryPoint: config.getString('entryPoint'), + logoutUrl: config.getOptionalString('logoutUrl'), + audience: config.getOptionalString('audience'), + issuer: config.getString('issuer'), + cert: config.getString('cert'), + privateKey: config.getOptionalString('privateKey'), + authnContext: config.getOptionalStringArray('authnContext'), + identifierFormat: config.getOptionalString('identifierFormat'), + decryptionPvk: config.getOptionalString('decryptionPvk'), + signatureAlgorithm: config.getOptionalString('signatureAlgorithm') as + | SignatureAlgorithm + | undefined, + digestAlgorithm: config.getOptionalString('digestAlgorithm'), + acceptedClockSkewMs: config.getOptionalNumber('acceptedClockSkewMs'), + + appUrl: globalConfig.appUrl, + authHandler, + signInResolver: options?.signIn?.resolver, + resolverContext, + }); + }; + }, +}); + +/** + * @public + * @deprecated Use `providers.saml.create` instead + */ +export const createSamlProvider = saml.create;