diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx
index 9e9ca1edd0..d4d0cc0490 100644
--- a/packages/app/src/App.tsx
+++ b/packages/app/src/App.tsx
@@ -19,25 +19,20 @@ import {
AlertDisplay,
OAuthRequestDialog,
SignInPage,
- useApi,
- configApiRef,
} from '@backstage/core';
import React, { FC } from 'react';
import Root from './components/Root';
import * as plugins from './plugins';
import { apis } from './apis';
import { hot } from 'react-hot-loader/root';
+import { providers } from './authProviders';
const app = createApp({
apis,
plugins: Object.values(plugins),
components: {
SignInPage: props => {
- const configApi = useApi(configApiRef);
- const providersConfig = configApi.getOptionalConfig('auth.providers');
- const providers = providersConfig?.keys() ?? [];
-
- return ;
+ return ;
},
},
});
diff --git a/packages/app/src/authProviders.ts b/packages/app/src/authProviders.ts
new file mode 100644
index 0000000000..d95848e014
--- /dev/null
+++ b/packages/app/src/authProviders.ts
@@ -0,0 +1,64 @@
+/*
+ * 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 {
+ googleAuthApiRef,
+ gitlabAuthApiRef,
+ oktaAuthApiRef,
+ githubAuthApiRef,
+ commonProvider,
+ customProvider,
+ guestProvider,
+} from '@backstage/core';
+
+export const providers = [
+ {
+ id: 'google-auth-provider',
+ title: 'Google',
+ message: 'Sign In using Google',
+ apiRef: googleAuthApiRef,
+ provider: commonProvider,
+ },
+ {
+ id: 'gitlab-auth-provider',
+ title: 'Gitlab',
+ message: 'Sign In using Gitlab',
+ apiRef: gitlabAuthApiRef,
+ provider: commonProvider,
+ },
+ {
+ id: 'github-auth-provider',
+ title: 'Github',
+ message: 'Sign In using Github',
+ apiRef: githubAuthApiRef,
+ provider: commonProvider,
+ },
+ {
+ id: 'okta-auth-provider',
+ title: 'Okta',
+ message: 'Sign In using Okta',
+ apiRef: oktaAuthApiRef,
+ provider: commonProvider,
+ },
+ {
+ id: 'guest-provider',
+ provider: guestProvider,
+ },
+ {
+ id: 'custom-provider',
+ provider: customProvider,
+ },
+];
diff --git a/packages/core/src/layout/SignInPage/SignInPage.tsx b/packages/core/src/layout/SignInPage/SignInPage.tsx
index 8a1191f442..e07e6490ae 100644
--- a/packages/core/src/layout/SignInPage/SignInPage.tsx
+++ b/packages/core/src/layout/SignInPage/SignInPage.tsx
@@ -21,11 +21,12 @@ import { Content } from '../Content/Content';
import { ContentHeader } from '../ContentHeader/ContentHeader';
import { Grid } from '@material-ui/core';
import { SignInPageProps, useApi, configApiRef } from '@backstage/core-api';
-import { useSignInProviders, SignInProviderId } from './providers';
+import { useSignInProviders } from './providers';
+import { SignInProviderConfig } from './types';
import { Progress } from '../../components/Progress';
export type Props = SignInPageProps & {
- providers: SignInProviderId[];
+ providers: SignInProviderConfig[];
};
export const SignInPage: FC = ({ onResult, providers }) => {
diff --git a/packages/core/src/layout/SignInPage/commonProvider.tsx b/packages/core/src/layout/SignInPage/commonProvider.tsx
new file mode 100644
index 0000000000..da0247fd6f
--- /dev/null
+++ b/packages/core/src/layout/SignInPage/commonProvider.tsx
@@ -0,0 +1,94 @@
+/*
+ * 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 React from 'react';
+import { Grid, Typography, Button } from '@material-ui/core';
+import { InfoCard } from '../InfoCard/InfoCard';
+import {
+ ProviderComponent,
+ ProviderLoader,
+ SignInProvider,
+ CommonSignInConfig,
+} from './types';
+import { useApi, errorApiRef } from '@backstage/core-api';
+
+const Component: ProviderComponent = ({ provider, onResult }) => {
+ const { apiRef, title, message } = provider as CommonSignInConfig;
+ const authApi = useApi(apiRef);
+ const errorApi = useApi(errorApiRef);
+
+ const handleLogin = async () => {
+ try {
+ const identity = await authApi.getBackstageIdentity({
+ instantPopup: true,
+ });
+
+ const profile = await authApi.getProfile();
+ onResult({
+ userId: identity!.id,
+ profile: profile!,
+ getIdToken: () => {
+ return authApi.getBackstageIdentity().then(i => i!.idToken);
+ },
+ logout: async () => {
+ await authApi.logout();
+ },
+ });
+ } catch (error) {
+ errorApi.post(error);
+ }
+ };
+
+ return (
+
+
+ Sign In
+
+ }
+ >
+ {message}
+
+
+ );
+};
+
+const loader: ProviderLoader = async (apis, apiRef) => {
+ const authApi = apis.get(apiRef)!;
+
+ const identity = await authApi.getBackstageIdentity({
+ optional: true,
+ });
+
+ if (!identity) {
+ return undefined;
+ }
+
+ const profile = await authApi.getProfile();
+
+ return {
+ userId: identity.id,
+ profile: profile!,
+ getIdToken: () => authApi.getBackstageIdentity().then(i => i!.idToken),
+ logout: async () => {
+ await authApi.logout();
+ },
+ };
+};
+
+export const commonProvider: SignInProvider = { Component, loader };
diff --git a/packages/core/src/layout/SignInPage/index.ts b/packages/core/src/layout/SignInPage/index.ts
index 49f55aefc5..e58d6a85df 100644
--- a/packages/core/src/layout/SignInPage/index.ts
+++ b/packages/core/src/layout/SignInPage/index.ts
@@ -15,3 +15,6 @@
*/
export { SignInPage } from './SignInPage';
+export { commonProvider } from './commonProvider';
+export { guestProvider } from './guestProvider';
+export { customProvider } from './customProvider';
diff --git a/packages/core/src/layout/SignInPage/providers.tsx b/packages/core/src/layout/SignInPage/providers.tsx
index 1eab51e3ee..9ab742b516 100644
--- a/packages/core/src/layout/SignInPage/providers.tsx
+++ b/packages/core/src/layout/SignInPage/providers.tsx
@@ -15,12 +15,6 @@
*/
import React, { useLayoutEffect, useState, useMemo, useCallback } from 'react';
-import { guestProvider } from './guestProvider';
-import { googleProvider } from './googleProvider';
-import { customProvider } from './customProvider';
-import { gitlabProvider } from './gitlabProvider';
-import { oktaProvider } from './oktaProvider';
-import { githubProvider } from './githubProvider';
import {
SignInPageProps,
SignInResult,
@@ -28,24 +22,12 @@ import {
useApiHolder,
errorApiRef,
} from '@backstage/core-api';
-import { SignInProvider } from './types';
+import { SignInProviderConfig, CommonSignInConfig } from './types';
const PROVIDER_STORAGE_KEY = '@backstage/core:SignInPage:provider';
-// Separate list here to avoid exporting internal types
-export type SignInProviderId = 'guest' | string;
-
-const signInProviders: { [id in SignInProviderId]: SignInProvider } = {
- guest: guestProvider,
- google: googleProvider,
- gitlab: gitlabProvider,
- oauth2: customProvider,
- okta: oktaProvider,
- github: githubProvider,
-};
-
export const useSignInProviders = (
- providers: SignInProviderId[],
+ providers: SignInProviderConfig[],
onResult: SignInPageProps['onResult'],
) => {
const errorApi = useApi(errorApiRef);
@@ -74,25 +56,29 @@ export const useSignInProviders = (
}
// We can't use storageApi here, as it might have a dependency on the IdentityApi
- const selectedProvider = localStorage.getItem(
+ const selectedProviderId = localStorage.getItem(
PROVIDER_STORAGE_KEY,
- ) as SignInProviderId;
+ ) as string;
// No provider selected, let the user pick one
- if (selectedProvider === null) {
+ if (selectedProviderId === null) {
setLoading(false);
return undefined;
}
- const provider = signInProviders[selectedProvider];
- if (!provider) {
+ const config = providers.find(
+ provider => provider.id === selectedProviderId,
+ );
+ if (!config) {
setLoading(false);
return undefined;
}
let didCancel = false;
- provider
- .loader(apiHolder)
+ const { apiRef } = config as CommonSignInConfig;
+
+ config.provider
+ .loader(apiHolder, apiRef)
.then(result => {
if (didCancel) {
return;
@@ -119,20 +105,22 @@ export const useSignInProviders = (
// This renders all available sign-in providers
const elements = useMemo(
() =>
- providers.map(providerId => {
- const provider = signInProviders[providerId];
- if (!provider) {
- throw new Error(`Unknown sign-in provider: ${providerId}`);
- }
- const { Component } = provider;
+ providers.map(config => {
+ const { Component } = config.provider;
const handleResult = (result: SignInResult) => {
- localStorage.setItem(PROVIDER_STORAGE_KEY, providerId);
+ localStorage.setItem(PROVIDER_STORAGE_KEY, config.id);
handleWrappedResult(result);
};
- return ;
+ return (
+
+ );
}),
[providers, handleWrappedResult],
);
diff --git a/packages/core/src/layout/SignInPage/types.ts b/packages/core/src/layout/SignInPage/types.ts
index e13cda5ddd..089ebac823 100644
--- a/packages/core/src/layout/SignInPage/types.ts
+++ b/packages/core/src/layout/SignInPage/types.ts
@@ -15,12 +15,46 @@
*/
import { ComponentType } from 'react';
-import { SignInPageProps, SignInResult, ApiHolder } from '@backstage/core-api';
+import {
+ SignInPageProps,
+ SignInResult,
+ ApiHolder,
+ ApiRef,
+ OAuthApi,
+ ProfileInfoApi,
+ BackstageIdentityApi,
+ SessionStateApi,
+ OpenIdConnectApi,
+} from '@backstage/core-api';
-export type ProviderComponent = ComponentType;
+export type SignInConfig = {
+ id: string;
+ provider: SignInProvider;
+};
+
+export type CommonSignInConfig = SignInConfig & {
+ title: string;
+ message: string;
+ apiRef: ApiRef<
+ OAuthApi &
+ ProfileInfoApi &
+ BackstageIdentityApi &
+ SessionStateApi &
+ OpenIdConnectApi
+ >;
+};
+
+export type SignInProviderConfig = SignInConfig | CommonSignInConfig;
+
+export type ProviderComponent = ComponentType<
+ SignInPageProps & { provider: SignInProviderConfig }
+>;
export type ProviderLoader = (
apis: ApiHolder,
+ apiRef: ApiRef<
+ OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionStateApi
+ >,
) => Promise;
export type SignInProvider = {