diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx
index c569308fc7..33c56796f5 100644
--- a/packages/app/src/App.tsx
+++ b/packages/app/src/App.tsx
@@ -33,7 +33,7 @@ const app = createApp({
SignInPage: props => (
),
},
diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts
index cac3d6e171..5bb17fd9c8 100644
--- a/packages/app/src/apis.ts
+++ b/packages/app/src/apis.ts
@@ -29,6 +29,7 @@ import {
OAuth2,
OktaAuth,
GitlabAuth,
+ Auth0Auth,
oauthRequestApiRef,
OAuthRequestManager,
googleAuthApiRef,
@@ -36,6 +37,7 @@ import {
oauth2ApiRef,
oktaAuthApiRef,
gitlabAuthApiRef,
+ auth0AuthApiRef,
storageApiRef,
WebStorage,
} from '@backstage/core';
@@ -129,6 +131,15 @@ export const apis = (config: ConfigApi) => {
oauthRequestApi,
}),
);
+
+ builder.add(
+ auth0AuthApiRef,
+ Auth0Auth.create({
+ apiOrigin: backendUrl,
+ basePath: '/auth/',
+ oauthRequestApi,
+ }),
+ );
builder.add(
oauth2ApiRef,
diff --git a/packages/core-api/src/apis/definitions/auth.ts b/packages/core-api/src/apis/definitions/auth.ts
index ec7aba81c4..05c4ed48af 100644
--- a/packages/core-api/src/apis/definitions/auth.ts
+++ b/packages/core-api/src/apis/definitions/auth.ts
@@ -264,6 +264,17 @@ export const gitlabAuthApiRef = createApiRef<
description: 'Provides authentication towards Gitlab APIs',
});
+/**
+ * Provides authentication towards Auth0 APIs.
+ *
+ * See https://auth0.com/docs/scopes/current/oidc-scopes
+ * for a full list of supported scopes.
+ */
+export const auth0AuthApiRef = createApiRef({
+ id: 'core.auth.auth0',
+ description: 'Provides authentication towards Auth0 APIs',
+});
+
/**
* Provides authentication for custom identity providers.
*/
diff --git a/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.test.ts b/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.test.ts
new file mode 100644
index 0000000000..5912c2a364
--- /dev/null
+++ b/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.test.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Auth0Auth from './Auth0Auth';
+
+describe('Auth0Auth', () => {
+ it('should normalize scope', () => {
+ const tests = [
+ {
+ arguments: ['read_user api write_repository'],
+ expect: new Set(['read_user', 'api', 'write_repository']),
+ },
+ {
+ arguments: ['read_repository sudo'],
+ expect: new Set(['read_repository', 'sudo']),
+ },
+ ];
+
+ for (const test of tests) {
+ expect(Auth0Auth.normalizeScopes(...test.arguments)).toEqual(test.expect);
+ }
+ });
+});
diff --git a/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts b/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts
new file mode 100644
index 0000000000..3d8a42a4cc
--- /dev/null
+++ b/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Auth0Icon from '@material-ui/icons/AcUnit';
+import { DefaultAuthConnector } from '../../../../lib/AuthConnector';
+import { Auth0Session } from './types';
+import {
+ OpenIdConnectApi,
+ ProfileInfoApi,
+ ProfileInfo,
+ SessionStateApi,
+ SessionState,
+ BackstageIdentityApi,
+ AuthRequestOptions,
+ BackstageIdentity,
+} from '../../../definitions/auth';
+import { OAuthRequestApi, AuthProvider } from '../../../definitions';
+import { SessionManager } from '../../../../lib/AuthSessionManager/types';
+import { RefreshingAuthSessionManager } from '../../../../lib/AuthSessionManager';
+import { Observable } from '../../../../types';
+
+type CreateOptions = {
+ // TODO(Following the words of Rugvip): These two should be grabbed from global config when available, they're not unique to Auth0Auth
+ apiOrigin: string;
+ basePath: string;
+
+ oauthRequestApi: OAuthRequestApi;
+
+ environment?: string;
+ provider?: AuthProvider & { id: string };
+};
+
+export type Auth0AuthResponse = {
+ providerInfo: {
+ accessToken: string;
+ idToken: string;
+ scope: string;
+ expiresInSeconds: number;
+ };
+ profile: ProfileInfo;
+ backstageIdentity: BackstageIdentity;
+};
+
+const DEFAULT_PROVIDER = {
+ id: 'auth0',
+ title: 'Auth0',
+ icon: Auth0Icon,
+};
+
+class Auth0Auth
+ implements
+ OpenIdConnectApi,
+ ProfileInfoApi,
+ BackstageIdentityApi,
+ SessionStateApi {
+ static create({
+ apiOrigin,
+ basePath,
+ environment = 'development',
+ provider = DEFAULT_PROVIDER,
+ oauthRequestApi,
+ }: CreateOptions) {
+ const connector = new DefaultAuthConnector({
+ apiOrigin,
+ basePath,
+ environment,
+ provider,
+ oauthRequestApi: oauthRequestApi,
+ sessionTransform(res: Auth0AuthResponse): Auth0Session {
+ return {
+ ...res,
+ providerInfo: {
+ idToken: res.providerInfo.idToken,
+ accessToken: res.providerInfo.accessToken,
+ scopes: Auth0Auth.normalizeScopes(res.providerInfo.scope),
+ expiresAt: new Date(
+ Date.now() + res.providerInfo.expiresInSeconds * 1000,
+ ),
+ },
+ };
+ },
+ });
+
+ const sessionManager = new RefreshingAuthSessionManager({
+ connector,
+ defaultScopes: new Set([
+ 'openid',
+ `email`,
+ `profile`,
+ ]),
+ sessionScopes: (session: Auth0Session) => session.providerInfo.scopes,
+ sessionShouldRefresh: (session: Auth0Session) => {
+ const expiresInSec =
+ (session.providerInfo.expiresAt.getTime() - Date.now()) / 1000;
+ return expiresInSec < 60 * 5;
+ },
+ });
+
+ return new Auth0Auth(sessionManager);
+ }
+
+ sessionState$(): Observable {
+ return this.sessionManager.sessionState$();
+ }
+
+ constructor(private readonly sessionManager: SessionManager) {}
+
+ async getIdToken(options: AuthRequestOptions = {}) {
+ const session = await this.sessionManager.getSession(options);
+ return session?.providerInfo.idToken ?? '';
+ }
+
+ async logout() {
+ await this.sessionManager.removeSession();
+ }
+
+ async getBackstageIdentity(
+ options: AuthRequestOptions = {},
+ ): Promise {
+ const session = await this.sessionManager.getSession(options);
+ return session?.backstageIdentity;
+ }
+
+ async getProfile(options: AuthRequestOptions = {}) {
+ const session = await this.sessionManager.getSession(options);
+ return session?.profile;
+ }
+
+ static normalizeScopes(scope?: string | string[]): Set {
+ if (!scope) {
+ return new Set();
+ }
+
+ const scopeList = Array.isArray(scope)
+ ? scope
+ : scope.split(/[\s|,]/).filter(Boolean);
+
+ return new Set(scopeList);
+ }
+}
+export default Auth0Auth;
diff --git a/packages/core-api/src/apis/implementations/auth/auth0/index.ts b/packages/core-api/src/apis/implementations/auth/auth0/index.ts
new file mode 100644
index 0000000000..fb5d432642
--- /dev/null
+++ b/packages/core-api/src/apis/implementations/auth/auth0/index.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export * from './types';
+export { default as Auth0Auth } from './Auth0Auth';
diff --git a/packages/core-api/src/apis/implementations/auth/auth0/types.ts b/packages/core-api/src/apis/implementations/auth/auth0/types.ts
new file mode 100644
index 0000000000..203ccd9fd9
--- /dev/null
+++ b/packages/core-api/src/apis/implementations/auth/auth0/types.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { ProfileInfo, BackstageIdentity } from '../../../definitions';
+
+export type Auth0Session = {
+ providerInfo: {
+ idToken: string;
+ accessToken: string;
+ scopes: Set;
+ expiresAt: Date;
+ };
+ profile: ProfileInfo;
+ backstageIdentity: BackstageIdentity;
+};
diff --git a/packages/core-api/src/apis/implementations/auth/index.ts b/packages/core-api/src/apis/implementations/auth/index.ts
index 786e9fa771..ce6e0d8570 100644
--- a/packages/core-api/src/apis/implementations/auth/index.ts
+++ b/packages/core-api/src/apis/implementations/auth/index.ts
@@ -19,3 +19,4 @@ export * from './gitlab';
export * from './google';
export * from './oauth2';
export * from './okta';
+export * from './auth0';
diff --git a/packages/core/src/layout/Sidebar/UserSettings.tsx b/packages/core/src/layout/Sidebar/UserSettings.tsx
index 5f66fc6558..89dd9f5a39 100644
--- a/packages/core/src/layout/Sidebar/UserSettings.tsx
+++ b/packages/core/src/layout/Sidebar/UserSettings.tsx
@@ -21,6 +21,7 @@ import {
identityApiRef,
oauth2ApiRef,
oktaAuthApiRef,
+ auth0AuthApiRef,
useApi,
} from '@backstage/core-api';
import Collapse from '@material-ui/core/Collapse';
@@ -69,6 +70,11 @@ export function SidebarUserSettings() {
apiRef={oktaAuthApiRef}
icon={Star}
/>
+
{
+ const auth0AuthApi = useApi(auth0AuthApiRef);
+ const errorApi = useApi(errorApiRef);
+
+ const handleLogin = async () => {
+ try {
+ const identity = await auth0AuthApi.getBackstageIdentity({
+ instantPopup: true,
+ });
+
+ const profile = await auth0AuthApi.getProfile();
+
+ onResult({
+ userId: identity!.id,
+ profile: profile!,
+ getIdToken: () =>
+ auth0AuthApi.getBackstageIdentity().then(i => i!.idToken),
+ logout: async () => {
+ await auth0AuthApi.logout();
+ },
+ });
+ } catch (error) {
+ errorApi.post(error);
+ }
+ };
+
+ return (
+
+
+ Sign In
+
+ }
+ >
+ Sign In using Auth0
+
+
+ );
+};
+
+const loader: ProviderLoader = async apis => {
+ const auth0AuthApi = apis.get(auth0AuthApiRef)!;
+
+ const identity = await auth0AuthApi.getBackstageIdentity({
+ optional: true,
+ });
+
+ if (!identity) {
+ return undefined;
+ }
+
+ const profile = await auth0AuthApi.getProfile();
+
+ return {
+ userId: identity.id,
+ profile: profile!,
+ getIdToken: () =>
+ auth0AuthApi.getBackstageIdentity().then(i => i!.idToken),
+ logout: async () => {
+ await auth0AuthApi.logout();
+ },
+ };
+};
+
+export const auth0Provider: SignInProvider = { Component, loader };
diff --git a/packages/core/src/layout/SignInPage/providers.tsx b/packages/core/src/layout/SignInPage/providers.tsx
index e4f4aee8c7..4eb3fe9222 100644
--- a/packages/core/src/layout/SignInPage/providers.tsx
+++ b/packages/core/src/layout/SignInPage/providers.tsx
@@ -21,6 +21,7 @@ import { customProvider } from './customProvider';
import { gitlabProvider } from './gitlabProvider';
import { oktaProvider } from './oktaProvider';
import { githubProvider } from './githubProvider';
+import { auth0Provider } from './auth0Provider';
import {
SignInPageProps,
SignInResult,
@@ -39,7 +40,8 @@ export type SignInProviderId =
| 'gitlab'
| 'custom'
| 'okta'
- | 'github';
+ | 'github'
+ | 'auth0';
const signInProviders: { [id in SignInProviderId]: SignInProvider } = {
guest: guestProvider,
@@ -48,6 +50,7 @@ const signInProviders: { [id in SignInProviderId]: SignInProvider } = {
custom: customProvider,
okta: oktaProvider,
github: githubProvider,
+ auth0: auth0Provider,
};
export const useSignInProviders = (
diff --git a/packages/dev-utils/src/devApp/apiFactories.ts b/packages/dev-utils/src/devApp/apiFactories.ts
index 35295b4631..b88611f062 100644
--- a/packages/dev-utils/src/devApp/apiFactories.ts
+++ b/packages/dev-utils/src/devApp/apiFactories.ts
@@ -30,6 +30,8 @@ import {
githubAuthApiRef,
GitlabAuth,
gitlabAuthApiRef,
+ Auth0Auth,
+ auth0AuthApiRef,
} from '@backstage/core';
// TODO(rugvip): We should likely figure out how to reuse all of these between apps
@@ -88,3 +90,14 @@ export const gitlabAuthApiFactory = createApiFactory({
oauthRequestApi,
}),
});
+
+export const auth0AuthApiFactory = createApiFactory({
+ implements: auth0AuthApiRef,
+ deps: { oauthRequestApi: oauthRequestApiRef },
+ factory: ({ oauthRequestApi }) =>
+ Auth0Auth.create({
+ apiOrigin: 'http://localhost:7000',
+ basePath: '/auth/',
+ oauthRequestApi,
+ }),
+});
diff --git a/packages/storybook/.storybook/apis.js b/packages/storybook/.storybook/apis.js
index f4b7557b89..ab461faabf 100644
--- a/packages/storybook/.storybook/apis.js
+++ b/packages/storybook/.storybook/apis.js
@@ -18,6 +18,8 @@ import {
OAuthRequestManager,
OktaAuth,
oktaAuthApiRef,
+ Auth0Auth,
+ auth0AuthApiRef,
} from '@backstage/core';
const builder = ApiRegistry.builder();
@@ -74,6 +76,15 @@ builder.add(
}),
);
+builder.add(
+ auth0AuthApiRef,
+ Auth0Auth.create({
+ apiOrigin: 'http://localhost:7000',
+ basePath: '/auth/',
+ oauthRequestApi,
+ }),
+);
+
builder.add(
oauth2ApiRef,
OAuth2.create({
diff --git a/plugins/auth-backend/README.md b/plugins/auth-backend/README.md
index ff0b1c7a3d..c4db98be94 100644
--- a/plugins/auth-backend/README.md
+++ b/plugins/auth-backend/README.md
@@ -48,6 +48,14 @@ export AUTH_OKTA_CLIENT_ID=x
export AUTH_OKTA_CLIENT_SECRET=x
```
+### Auth0
+
+```bash
+export AUTH_AUTH0_DOMAIN=x
+export AUTH_AUTH0_CLIENT_ID=x
+export AUTH_AUTH0_CLIENT_SECRET=x
+```
+
### SAML
To try out SAML, you can use the mock identity provider:
diff --git a/plugins/auth-backend/src/providers/auth0/index.ts b/plugins/auth-backend/src/providers/auth0/index.ts
new file mode 100644
index 0000000000..87c9aceaa4
--- /dev/null
+++ b/plugins/auth-backend/src/providers/auth0/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export { createAuth0Provider } from './provider';
diff --git a/plugins/auth-backend/src/providers/auth0/provider.ts b/plugins/auth-backend/src/providers/auth0/provider.ts
new file mode 100644
index 0000000000..8eebd5f158
--- /dev/null
+++ b/plugins/auth-backend/src/providers/auth0/provider.ts
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import express from 'express';
+import passport from 'passport';
+import Auth0Strategy, { Auth0StrategyOptionsWithRequest } from './strategy';
+import { Logger } from 'winston';
+import { TokenIssuer } from '../../identity';
+import {
+ EnvironmentHandler,
+ EnvironmentHandlers,
+} from '../../lib/EnvironmentHandler';
+import { OAuthProvider } from '../../lib/OAuthProvider';
+import {
+ executeFetchUserProfileStrategy,
+ executeFrameHandlerStrategy,
+ executeRedirectStrategy,
+ executeRefreshTokenStrategy,
+ makeProfileInfo,
+} from '../../lib/PassportStrategyHelper';
+import {
+ AuthProviderConfig,
+ EnvironmentProviderConfig,
+ OAuthProviderHandlers,
+ OAuthResponse,
+ PassportDoneCallback,
+ RedirectInfo,
+} from '../types';
+
+interface Auth0ProviderConfig {
+ clientId: string;
+ clientSecret: string;
+ domain: string;
+ callbackURL: string;
+ secure: boolean;
+ appOrigin: string;
+}
+
+type PrivateInfo = {
+ refreshToken: string;
+};
+
+export class Auth0AuthProvider implements OAuthProviderHandlers {
+ private readonly _strategy: Auth0Strategy;
+
+ constructor(options: Auth0StrategyOptionsWithRequest) {
+ this._strategy = new Auth0Strategy(
+ options,
+ (
+ accessToken: any,
+ refreshToken: any,
+ params: any,
+ rawProfile: passport.Profile,
+ done: PassportDoneCallback,
+ ) => {
+ const profile = makeProfileInfo(rawProfile, params.id_token);
+ done(
+ undefined,
+ {
+ providerInfo: {
+ idToken: params.id_token,
+ accessToken,
+ scope: params.scope,
+ expiresInSeconds: params.expires_in,
+ },
+ profile,
+ },
+ {
+ refreshToken,
+ },
+ );
+ },
+ );
+ }
+
+ async start(
+ req: express.Request,
+ options: Record,
+ ): Promise {
+ const providerOptions = {
+ ...options,
+ accessType: 'offline',
+ prompt: 'consent',
+ };
+ return await executeRedirectStrategy(req, this._strategy, providerOptions);
+ }
+
+ async handler(
+ req: express.Request,
+ ): Promise<{ response: OAuthResponse; refreshToken: string }> {
+ const { response, privateInfo } = await executeFrameHandlerStrategy<
+ OAuthResponse,
+ PrivateInfo
+ >(req, this._strategy);
+
+ return {
+ response: await this.populateIdentity(response),
+ refreshToken: privateInfo.refreshToken,
+ };
+ }
+
+ async refresh(refreshToken: string, scope: string): Promise {
+ const { accessToken, params } = await executeRefreshTokenStrategy(
+ this._strategy,
+ refreshToken,
+ scope,
+ );
+
+ const profile = await executeFetchUserProfileStrategy(
+ this._strategy,
+ accessToken,
+ params.id_token,
+ );
+
+ return this.populateIdentity({
+ providerInfo: {
+ accessToken,
+ idToken: params.id_token,
+ expiresInSeconds: params.expires_in,
+ scope: params.scope,
+ },
+ profile,
+ });
+ }
+
+ // Use this function to grab the user profile info from the token
+ // Then populate the profile with it
+ private async populateIdentity(
+ response: OAuthResponse,
+ ): Promise {
+ const { profile } = response;
+
+ if (!profile.email) {
+ throw new Error('Profile does not contain a profile');
+ }
+
+ const id = profile.email.split('@')[0];
+
+ return { ...response, backstageIdentity: { id } };
+ }
+}
+
+export function createAuth0Provider(
+ { baseUrl }: AuthProviderConfig,
+ providerConfig: EnvironmentProviderConfig,
+ logger: Logger,
+ tokenIssuer: TokenIssuer,
+) {
+ const envProviders: EnvironmentHandlers = {};
+ const providerId = 'auth0';
+
+ for (const [env, envConfig] of Object.entries(providerConfig)) {
+ const config = (envConfig as unknown) as Auth0ProviderConfig;
+ const opts = {
+ clientID: config.clientId,
+ clientSecret: config.clientSecret,
+ callbackURL: `${baseUrl}/${providerId}/handler/frame?env=${env}`,
+ domain: config.domain,
+ } as Auth0StrategyOptionsWithRequest;
+
+ if (!opts.clientID || !opts.clientSecret || !opts.domain) {
+ if (process.env.NODE_ENV !== 'development') {
+ throw new Error(
+ 'Failed to initialize OAuth2 auth provider, set AUTH_AUTH0_CLIENT_ID, AUTH_AUTH0_CLIENT_SECRET, and AUTH_AUTH0_DOMAIN env vars',
+ );
+ }
+
+ logger.warn(
+ 'OAuth2 auth provider disabled, set AUTH_AUTH0_CLIENT_ID, AUTH_AUTH0_CLIENT_SECRET, and AUTH_AUTH0_DOMAIN env vars to enable',
+ );
+ continue;
+ }
+
+ const { secure, appOrigin } = config;
+ envProviders[env] = new OAuthProvider(new Auth0AuthProvider(opts), {
+ disableRefresh: true,
+ persistScopes: true,
+ providerId: providerId,
+ secure,
+ baseUrl,
+ appOrigin,
+ tokenIssuer,
+ });
+ }
+
+ return new EnvironmentHandler(providerId, envProviders);
+}
diff --git a/plugins/auth-backend/src/providers/auth0/strategy.ts b/plugins/auth-backend/src/providers/auth0/strategy.ts
new file mode 100644
index 0000000000..4dd7440448
--- /dev/null
+++ b/plugins/auth-backend/src/providers/auth0/strategy.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import OAuth2Strategy from 'passport-oauth2';
+
+export interface Auth0StrategyOptionsWithRequest {
+ clientID: string;
+ clientSecret: string;
+ callbackURL: string;
+ domain: string;
+ passReqToCallback: true;
+}
+
+export default class Auth0Strategy extends OAuth2Strategy {
+ constructor(options: Auth0StrategyOptionsWithRequest, verify: OAuth2Strategy.VerifyFunctionWithRequest) {
+ const optionsWithURLs = {
+ ...options,
+ authorizationURL: `https://${options.domain}/authorize`,
+ tokenURL: `https://${options.domain}/oauth/token`,
+ userInfoURL: `https://${options.domain}/userinfo`,
+ apiUrl: `https://${options.domain}/api`,
+ }
+ super(optionsWithURLs, verify)
+ }
+}
diff --git a/plugins/auth-backend/src/providers/factories.ts b/plugins/auth-backend/src/providers/factories.ts
index 4c9caf214b..77e5356721 100644
--- a/plugins/auth-backend/src/providers/factories.ts
+++ b/plugins/auth-backend/src/providers/factories.ts
@@ -23,6 +23,7 @@ import { createGoogleProvider } from './google';
import { createOAuth2Provider } from './oauth2';
import { createOktaProvider } from './okta';
import { createSamlProvider } from './saml';
+import { createAuth0Provider } from './auth0'
import { AuthProviderConfig, AuthProviderFactory } from './types';
const factories: { [providerId: string]: AuthProviderFactory } = {
@@ -31,6 +32,7 @@ const factories: { [providerId: string]: AuthProviderFactory } = {
gitlab: createGitlabProvider,
saml: createSamlProvider,
okta: createOktaProvider,
+ auth0: createAuth0Provider,
oauth2: createOAuth2Provider,
};
diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts
index 6b296cba2d..256545219d 100644
--- a/plugins/auth-backend/src/service/router.ts
+++ b/plugins/auth-backend/src/service/router.ts
@@ -113,6 +113,15 @@ export async function createRouter(
tokenURL: process.env.AUTH_OAUTH2_TOKEN_URL!,
},
},
+ auth0: {
+ development: {
+ appOrigin,
+ secure: false,
+ domain: process.env.AUTH_AUTH0_DOMAIN!,
+ clientId: process.env.AUTH_AUTH0_CLIENT_ID!,
+ clientSecret: process.env.AUTH_AUTH0_CLIENT_SECRET!
+ },
+ },
},
},
};