From 9b5fa0da86faa63672403f824485cc378ed9e2c0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 16 Jun 2020 16:52:05 +0200 Subject: [PATCH] packages/core: pluggable sign-in providers and re-loading of session --- .../core/src/layout/SignInPage/SignInPage.tsx | 182 ++++++++++++------ 1 file changed, 126 insertions(+), 56 deletions(-) diff --git a/packages/core/src/layout/SignInPage/SignInPage.tsx b/packages/core/src/layout/SignInPage/SignInPage.tsx index f8879aa55b..2feb7be3bc 100644 --- a/packages/core/src/layout/SignInPage/SignInPage.tsx +++ b/packages/core/src/layout/SignInPage/SignInPage.tsx @@ -14,7 +14,13 @@ * limitations under the License. */ -import React, { FC, useLayoutEffect } from 'react'; +import React, { + FC, + useLayoutEffect, + useState, + ComponentType, + useMemo, +} from 'react'; import { Page } from '../Page'; import { Header } from '../Header'; import { Content } from '../Content/Content'; @@ -26,86 +32,150 @@ import { SignInResult, useApi, configApiRef, + useApiHolder, + ApiHolder, + errorApiRef, } from '@backstage/core-api'; const PROVIDER_STORAGE_KEY = '@backstage/core:SignInPage:provider'; -type ProviderProps = SignInPageProps & { - selected: boolean; +type ProviderComponent = ComponentType; + +type ProviderLoader = (apis: ApiHolder) => Promise; + +type SignInProvider = { + component: ProviderComponent; + loader: ProviderLoader; }; -const GuestProvider: FC = ({ selected, onResult }) => { - useLayoutEffect(() => { - if (selected) { - onResult({ userId: 'guest' }); - } - }, [selected, onResult]); +const GuestProvider: ProviderComponent = ({ onResult }) => ( + + onResult({ userId: 'guest' })} + > + Enter + + } + > + + Enter as a Guest User. +
+ You will not have a verified identity, +
+ meaning some features might be unavailable. +
+
+
+); - return ( - - onResult({ userId: 'guest' })} - > - Enter - - } - > - - Enter as a Guest User. -
- You will not have a verified identity, -
- so some features might be unavailable. -
-
-
- ); +const guestLoader: ProviderLoader = async () => { + return { userId: 'guest' }; }; -export type SignInProvider = 'guest'; +const guestProvider: SignInProvider = { + component: GuestProvider, + loader: guestLoader, +}; + +const signInProviders = { + guest: guestProvider, +}; + +export type SignInProviderId = keyof typeof signInProviders; export type Props = SignInPageProps & { - providers: SignInProvider[]; + providers: SignInProviderId[]; }; export const SignInPage: FC = ({ onResult, providers }) => { const configApi = useApi(configApiRef); + const errorApi = useApi(errorApiRef); + const apiHolder = useApiHolder(); // We can't use storageApi here, as it might have a dependency on the IdentityApi - const selectedProvider = localStorage.getItem(PROVIDER_STORAGE_KEY); + const selectedProvider = localStorage.getItem( + PROVIDER_STORAGE_KEY, + ) as SignInProviderId; - const makeResultHandler = (provider: SignInProvider) => ( - result: SignInResult, - ) => { - localStorage.setItem(PROVIDER_STORAGE_KEY, provider); + const [attempting, setAttempting] = useState(Boolean(selectedProvider)); - onResult({ - ...result, - logout: async () => { - localStorage.removeItem(PROVIDER_STORAGE_KEY); - await result.logout?.(); - }, - }); - }; + useLayoutEffect(() => { + if (!attempting || selectedProvider === null) { + return undefined; + } + + const provider = signInProviders[selectedProvider]; + if (!provider) { + setAttempting(false); + return undefined; + } + + let didCancel = false; + provider + .loader(apiHolder) + .then(result => { + if (didCancel) { + return; + } + setAttempting(false); + if (result) { + onResult({ + ...result, + logout: async () => { + localStorage.removeItem(PROVIDER_STORAGE_KEY); + await result.logout?.(); + }, + }); + } + }) + .catch(error => { + if (!didCancel) { + errorApi.post(error); + } + }); + + return () => { + didCancel = true; + }; + }, [attempting, errorApi, onResult, apiHolder, providers, selectedProvider]); + + const providerElements = useMemo( + () => + providers.map(providerId => { + const provider = signInProviders[providerId]; + if (!provider) { + throw new Error(`Unknown sign-in provider: ${providerId}`); + } + const { component: Component } = provider; + + const handleResult = (result: SignInResult) => { + localStorage.setItem(PROVIDER_STORAGE_KEY, providerId); + + onResult({ + ...result, + logout: async () => { + localStorage.removeItem(PROVIDER_STORAGE_KEY); + await result.logout?.(); + }, + }); + }; + + return ; + }), + [providers, onResult], + ); return (
- - {providers.includes('guest') && ( - - )} - + {providerElements} );