auth-backend: migrate all other providers to use integration helper

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2022-04-08 14:37:55 +02:00
parent 0d1f36cd6d
commit 01d4bd5109
11 changed files with 637 additions and 503 deletions
@@ -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<OAuthResult>;
/**
* 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<OAuthResult>;
};
}): 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<OAuthResult>;
const authHandler: AuthHandler<BitbucketOAuthResult> =
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<OAuthResult>;
};
}) {
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<BitbucketOAuthResult> =
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;
@@ -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<GcpIapResult>;
/**
* 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<GcpIapResult>;
};
}): AuthProviderFactory {
return ({ config, resolverContext }) => {
const audience = config.getString('audience');
authHandler?: AuthHandler<GcpIapResult>;
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<GcpIapResult>;
};
}) {
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;
@@ -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<GithubOAuthResult>;
/**
* 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<GithubOAuthResult>;
};
authHandler?: AuthHandler<GithubOAuthResult>;
/**
* 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<GithubOAuthResult>;
};
const authHandler: AuthHandler<GithubOAuthResult> = 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<GithubOAuthResult> = 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;
@@ -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<OAuthResult>;
/**
* 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<OAuthResult>;
/**
* 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<OAuthResult>;
};
}): 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<OAuthResult>;
};
}) {
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<OAuthResult> =
options?.authHandler ?? gitlabDefaultAuthHandler;
const authHandler: AuthHandler<OAuthResult> =
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;
@@ -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<OAuthResult>;
/**
* 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<OAuthResult>;
};
}): 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<OAuthResult>;
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<OAuthResult>;
};
}) {
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<OAuthResult> = 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<OAuthResult> = 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;
@@ -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<JWTPayload>
}
/**
* Factory function for oauth2-proxy auth provider
* Auth provider integration for oauth2-proxy auth
*
* @public
*/
export const createOauth2ProxyProvider =
<JWTPayload>(options: {
export const oauth2Proxy = createAuthProviderIntegration({
create<JWTPayload>(options: {
/**
* Configure an auth handler to generate a profile for the user.
*/
@@ -171,13 +171,21 @@ export const createOauth2ProxyProvider =
*/
resolver: SignInResolver<OAuth2ProxyResult<JWTPayload>>;
};
}): AuthProviderFactory =>
({ resolverContext }) => {
const signInResolver = options.signIn.resolver;
const authHandler = options.authHandler;
return new Oauth2ProxyAuthProvider<JWTPayload>({
resolverContext,
signInResolver,
authHandler,
});
};
}) {
return ({ resolverContext }) => {
const signInResolver = options.signIn.resolver;
const authHandler = options.authHandler;
return new Oauth2ProxyAuthProvider<JWTPayload>({
resolverContext,
signInResolver,
authHandler,
});
};
},
});
/**
* @public
* @deprecated Use `providers.oauth2Proxy.create` instead
*/
export const createOauth2ProxyProvider = oauth2Proxy.create;
@@ -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<OAuthResult>;
/**
* Auth provider integration for generic OAuth2 auth
*
* @public
*/
export const oauth2 = createAuthProviderIntegration({
create(options?: {
authHandler?: AuthHandler<OAuthResult>;
signIn?: {
resolver: SignInResolver<OAuthResult>;
};
}): 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<OAuthResult>;
};
}) {
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<OAuthResult> = options?.authHandler
? options.authHandler
: async ({ fullProfile, params }) => ({
profile: makeProfileInfo(fullProfile, params.id_token),
});
const authHandler: AuthHandler<OAuthResult> = 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;
@@ -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<OidcAuthResult>;
/**
* Auth provider integration for generic OpenID Connect auth
*
* @public
*/
export const oidc = createAuthProviderIntegration({
create(options?: {
authHandler?: AuthHandler<OidcAuthResult>;
signIn?: {
resolver: SignInResolver<OidcAuthResult>;
};
}): 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<OidcAuthResult>;
};
}) {
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<OidcAuthResult> = options?.authHandler
? options.authHandler
: async ({ userinfo }) => ({
profile: {
displayName: userinfo.name,
email: userinfo.email,
picture: userinfo.picture,
},
});
const authHandler: AuthHandler<OidcAuthResult> = 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;
@@ -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<OAuthResult>;
/**
* 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<OAuthResult>;
};
}): 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<OAuthResult>;
// 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<OAuthResult>;
};
}) {
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<OAuthResult> = _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<OAuthResult> = 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;
@@ -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<OAuthResult>;
/**
* 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<OAuthResult>;
};
}): 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<OAuthResult>;
const authHandler: AuthHandler<OAuthResult> = 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<OAuthResult>;
};
}) {
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<OAuthResult> = 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;
@@ -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<SamlAuthResult>;
/**
* 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<SamlAuthResult>;
};
}): AuthProviderFactory => {
return ({ providerId, globalConfig, config, resolverContext }) => {
const authHandler: AuthHandler<SamlAuthResult> = options?.authHandler
? options.authHandler
: async ({ fullProfile }) => ({
profile: {
email: fullProfile.email,
displayName: fullProfile.displayName,
},
});
authHandler?: AuthHandler<SamlAuthResult>;
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<SamlAuthResult>;
};
}) {
return ({ providerId, globalConfig, config, resolverContext }) => {
const authHandler: AuthHandler<SamlAuthResult> = 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;