Allow application to register custom AuthProviderFactory. Refs #1984 (#3329)

* Allow application to register custom `AuthProviderFactory`. Refs #1984

* PR comments: Explicit exports

* Export `OAuth` specific types from the package

* Update documentation on how to use the interfaces

* Re-export from base `index.ts` as per ADR-004

* pass `providerId` as a parameter to `AuthFactory` instead of hardcoding

Co-authored-by: Govindarajan Nagarajan <govindarajan.nagarajan@zalando.de>
This commit is contained in:
Govind
2020-11-26 11:27:00 +01:00
committed by GitHub
parent 0ea0fb7d6b
commit 202da8ab36
17 changed files with 148 additions and 61 deletions
+8
View File
@@ -15,3 +15,11 @@
*/
export * from './service/router';
export * from './providers';
// flow package provides 2 functions
// ensuresXRequestedWith and postMessageResponse to safely handle CORS requests for login. The WebMessageResponse type in flow is used to type the response from the login-popup
export * from './lib/flow';
// OAuth wrapper over a passport or a custom `startegy`.
export * from './lib/oauth';
@@ -15,3 +15,5 @@
*/
export { ensuresXRequestedWith, postMessageResponse } from './authFlowHelpers';
export type { WebMessageResponse } from './types';
@@ -149,12 +149,12 @@ export class Auth0AuthProvider implements OAuthHandlers {
}
export const createAuth0Provider: AuthProviderFactory = ({
providerId,
globalConfig,
config,
tokenIssuer,
}) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
const providerId = 'auth0';
const clientId = envConfig.getString('clientId');
const clientSecret = envConfig.getString('clientSecret');
const domain = envConfig.getString('domain');
@@ -24,9 +24,9 @@ import { createSamlProvider } from './saml';
import { createAuth0Provider } from './auth0';
import { createMicrosoftProvider } from './microsoft';
import { createOneLoginProvider } from './onelogin';
import { AuthProviderFactory, AuthProviderFactoryOptions } from './types';
import { AuthProviderFactory } from './types';
const factories: { [providerId: string]: AuthProviderFactory } = {
export const factories: { [providerId: string]: AuthProviderFactory } = {
google: createGoogleProvider,
github: createGithubProvider,
gitlab: createGitlabProvider,
@@ -38,15 +38,3 @@ const factories: { [providerId: string]: AuthProviderFactory } = {
oidc: createOidcProvider,
onelogin: createOneLoginProvider,
};
export function createAuthProvider(
providerId: string,
options: AuthProviderFactoryOptions,
) {
const factory = factories[providerId];
if (!factory) {
throw Error(`No auth provider available for '${providerId}'`);
}
return factory(options);
}
@@ -137,12 +137,12 @@ export class GithubAuthProvider implements OAuthHandlers {
}
export const createGithubProvider: AuthProviderFactory = ({
providerId,
globalConfig,
config,
tokenIssuer,
}) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
const providerId = 'github';
const clientId = envConfig.getString('clientId');
const clientSecret = envConfig.getString('clientSecret');
const enterpriseInstanceUrl = envConfig.getOptionalString(
@@ -140,12 +140,12 @@ export class GitlabAuthProvider implements OAuthHandlers {
}
export const createGitlabProvider: AuthProviderFactory = ({
providerId,
globalConfig,
config,
tokenIssuer,
}) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
const providerId = 'gitlab';
const clientId = envConfig.getString('clientId');
const clientSecret = envConfig.getString('clientSecret');
const audience = envConfig.getString('audience');
@@ -175,6 +175,7 @@ export class GoogleAuthProvider implements OAuthHandlers {
}
export const createGoogleProvider: AuthProviderFactory = ({
providerId,
globalConfig,
config,
logger,
@@ -182,7 +183,6 @@ export const createGoogleProvider: AuthProviderFactory = ({
catalogApi,
}) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
const providerId = 'google';
const clientId = envConfig.getString('clientId');
const clientSecret = envConfig.getString('clientSecret');
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
+13 -1
View File
@@ -14,4 +14,16 @@
* limitations under the License.
*/
export { createAuthProvider } from './factories';
export { factories as defaultAuthProviderFactories } from './factories';
// Export the minimal interface required for implementing a
// custom Authorization Handler
export type {
AuthProviderRouteHandlers,
AuthProviderFactoryOptions,
AuthProviderFactory,
} from './types';
// These types are needed for a postMessage from the login pop-up
// to the frontend
export type { AuthResponse, BackstageIdentity, ProfileInfo } from './types';
@@ -206,13 +206,12 @@ export class MicrosoftAuthProvider implements OAuthHandlers {
}
export const createMicrosoftProvider: AuthProviderFactory = ({
providerId,
globalConfig,
config,
tokenIssuer,
}) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
const providerId = 'microsoft';
const clientId = envConfig.getString('clientId');
const clientSecret = envConfig.getString('clientSecret');
const tenantID = envConfig.getString('tenantId');
@@ -157,12 +157,12 @@ export class OAuth2AuthProvider implements OAuthHandlers {
}
export const createOAuth2Provider: AuthProviderFactory = ({
providerId,
globalConfig,
config,
tokenIssuer,
}) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
const providerId = 'oauth2';
const clientId = envConfig.getString('clientId');
const clientSecret = envConfig.getString('clientSecret');
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
@@ -171,12 +171,12 @@ export class OidcAuthProvider implements OAuthHandlers {
}
export const createOidcProvider: AuthProviderFactory = ({
providerId,
globalConfig,
config,
tokenIssuer,
}) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
const providerId = 'oidc';
const clientId = envConfig.getString('clientId');
const clientSecret = envConfig.getString('clientSecret');
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
@@ -168,12 +168,12 @@ export class OktaAuthProvider implements OAuthHandlers {
}
export const createOktaProvider: AuthProviderFactory = ({
providerId,
globalConfig,
config,
tokenIssuer,
}) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
const providerId = 'okta';
const clientId = envConfig.getString('clientId');
const clientSecret = envConfig.getString('clientSecret');
const audience = envConfig.getString('audience');
@@ -147,12 +147,12 @@ export class OneLoginProvider implements OAuthHandlers {
}
export const createOneLoginProvider: AuthProviderFactory = ({
providerId,
globalConfig,
config,
tokenIssuer,
}) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
const providerId = 'onelogin';
const clientId = envConfig.getString('clientId');
const clientSecret = envConfig.getString('clientSecret');
const issuer = envConfig.getString('issuer');
@@ -121,12 +121,12 @@ type SAMLProviderOptions = {
};
export const createSamlProvider: AuthProviderFactory = ({
providerId,
globalConfig,
config,
tokenIssuer,
}) => {
const url = new URL(globalConfig.baseUrl);
const providerId = 'saml';
const entryPoint = config.getString('entryPoint');
const issuer = config.getString('issuer');
const opts = {
@@ -113,6 +113,7 @@ export interface AuthProviderRouteHandlers {
}
export type AuthProviderFactoryOptions = {
providerId: string;
globalConfig: AuthProviderConfig;
config: Config;
logger: Logger;
+23 -6
View File
@@ -14,6 +14,14 @@
* limitations under the License.
*/
import express from 'express';
import Router from 'express-promise-router';
import cookieParser from 'cookie-parser';
import { Logger } from 'winston';
import {
defaultAuthProviderFactories,
AuthProviderFactory,
} from '../providers';
import {
NotFoundError,
PluginDatabaseManager,
@@ -21,20 +29,18 @@ import {
} from '@backstage/backend-common';
import { CatalogClient } from '@backstage/catalog-client';
import { Config } from '@backstage/config';
import cookieParser from 'cookie-parser';
import express from 'express';
import Router from 'express-promise-router';
import { Logger } from 'winston';
import { createOidcRouter, DatabaseKeyStore, TokenFactory } from '../identity';
import { createAuthProvider } from '../providers';
import session from 'express-session';
import passport from 'passport';
type ProviderFactories = { [s: string]: AuthProviderFactory };
export interface RouterOptions {
logger: Logger;
database: PluginDatabaseManager;
config: Config;
discovery: PluginEndpointDiscovery;
providerFactories?: ProviderFactories;
}
export async function createRouter({
@@ -42,6 +48,7 @@ export async function createRouter({
config,
discovery,
database,
providerFactories,
}: RouterOptions): Promise<express.Router> {
const router = Router();
@@ -74,13 +81,23 @@ export async function createRouter({
router.use(express.urlencoded({ extended: false }));
router.use(express.json());
const allProviderFactories = {
...defaultAuthProviderFactories,
...providerFactories,
};
const providersConfig = config.getConfig('auth.providers');
const providers = providersConfig.keys();
for (const providerId of providers) {
logger.info(`Configuring provider, ${providerId}`);
try {
const provider = createAuthProvider(providerId, {
const providerFactory = allProviderFactories[providerId];
if (!providerFactory) {
throw Error(`No auth provider available for '${providerId}'`);
}
const provider = providerFactory({
providerId,
globalConfig: { baseUrl: authUrl, appUrl },
config: providersConfig.getConfig(providerId),
logger,