From 4c205d039d4a2fb630515fd5adf3f42b6bb0e45b Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Tue, 9 Jun 2020 23:30:39 +0200 Subject: [PATCH] refactoring to have side item components based on provider type --- .../core-api/src/apis/definitions/auth.ts | 8 +- .../implementations/auth/github/GithubAuth.ts | 8 +- .../implementations/auth/google/GoogleAuth.ts | 4 +- .../core/src/layout/Sidebar/UserSettings.tsx | 268 +++++++++++------- 4 files changed, 173 insertions(+), 115 deletions(-) diff --git a/packages/core-api/src/apis/definitions/auth.ts b/packages/core-api/src/apis/definitions/auth.ts index 4cbc8bcfb1..9735e65977 100644 --- a/packages/core-api/src/apis/definitions/auth.ts +++ b/packages/core-api/src/apis/definitions/auth.ts @@ -15,6 +15,7 @@ */ import { createApiRef } from '../ApiRef'; +import { Observable } from '../..'; /** * This file contains declarations for common interfaces of auth-related APIs. @@ -167,6 +168,9 @@ export type ProfileInfo = { picture?: string; }; +export type ObservableSession = { + session$(): Observable; +}; /** * Provides authentication towards Google APIs and identities. * @@ -176,7 +180,7 @@ export type ProfileInfo = { * email and expiration information. Do not rely on any other fields, as they might not be present. */ export const googleAuthApiRef = createApiRef< - OAuthApi & OpenIdConnectApi & ProfileInfoApi + OAuthApi & OpenIdConnectApi & ProfileInfoApi & ObservableSession >({ id: 'core.auth.google', description: 'Provides authentication towards Google APIs and identities', @@ -188,7 +192,7 @@ export const googleAuthApiRef = createApiRef< * See https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/ * for a full list of supported scopes. */ -export const githubAuthApiRef = createApiRef({ +export const githubAuthApiRef = createApiRef({ id: 'core.auth.github', description: 'Provides authentication towards Github APIs', }); diff --git a/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts b/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts index e9e40c2eab..7322ddeabf 100644 --- a/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts @@ -17,7 +17,11 @@ import GithubIcon from '@material-ui/icons/AcUnit'; import { DefaultAuthConnector } from '../../../../lib/AuthConnector'; import { GithubSession } from './types'; -import { OAuthApi, AccessTokenOptions } from '../../../definitions/auth'; +import { + OAuthApi, + AccessTokenOptions, + ObservableSession, +} from '../../../definitions/auth'; import { OAuthRequestApi, AuthProvider } from '../../../definitions'; import { SessionManager } from '../../../../lib/AuthSessionManager/types'; import { StaticAuthSessionManager } from '../../../../lib/AuthSessionManager'; @@ -48,7 +52,7 @@ const DEFAULT_PROVIDER = { icon: GithubIcon, }; -class GithubAuth implements OAuthApi { +class GithubAuth implements OAuthApi, ObservableSession { static create({ apiOrigin, basePath, diff --git a/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts b/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts index 0675c60e82..82048ee8ae 100644 --- a/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts @@ -25,6 +25,7 @@ import { ProfileInfoApi, ProfileInfoOptions, ProfileInfo, + ObservableSession, } from '../../../definitions/auth'; import { OAuthRequestApi, AuthProvider } from '../../../definitions'; import { SessionManager } from '../../../../lib/AuthSessionManager/types'; @@ -59,7 +60,8 @@ const DEFAULT_PROVIDER = { const SCOPE_PREFIX = 'https://www.googleapis.com/auth/'; -class GoogleAuth implements OAuthApi, OpenIdConnectApi, ProfileInfoApi { +class GoogleAuth + implements OAuthApi, OpenIdConnectApi, ProfileInfoApi, ObservableSession { static create({ apiOrigin, basePath, diff --git a/packages/core/src/layout/Sidebar/UserSettings.tsx b/packages/core/src/layout/Sidebar/UserSettings.tsx index 722d2d3f4f..e63235434a 100644 --- a/packages/core/src/layout/Sidebar/UserSettings.tsx +++ b/packages/core/src/layout/Sidebar/UserSettings.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { useState, useContext, useEffect, useRef } from 'react'; +import React, { useState, useContext, useEffect, useRef, FC } from 'react'; import Collapse from '@material-ui/core/Collapse'; import ExpandLess from '@material-ui/icons/ExpandLess'; import ExpandMore from '@material-ui/icons/ExpandMore'; @@ -29,76 +29,131 @@ import { googleAuthApiRef, githubAuthApiRef, ProfileInfo, + OAuthApi, + OpenIdConnectApi, + ProfileInfoApi, + ApiRef, + ObservableSession, } from '@backstage/core-api'; import { Avatar, IconButton, makeStyles, Tooltip } from '@material-ui/core'; import PowerButton from '@material-ui/icons/PowerSettingsNew'; -type AppAuthProviders = Provider[]; - type Provider = { title: string; - api: any; - identity?: boolean; - isSignedIn: boolean; icon: any; }; -const useProviders = () => { - const googleAuth = useApi(googleAuthApiRef); - const githubAuth = useApi(githubAuthApiRef); - // TODO(soapraj): List all the providers supported by the app - const [providers, setProviders] = useState([ - { - title: 'Google', - api: googleAuth, - identity: true, - isSignedIn: false, - icon: Star, - }, - { - title: 'Github', - api: githubAuth, - isSignedIn: false, - icon: StarBorder, - }, - ]); +type OAuthProviderSidebarProps = { + title: string; + icon: any; + apiRef: ApiRef; +}; - // On page load we check the status of sign-in/sign-out for all the providers - // by making a optional getIdToken or getAccessToken request. - const setIsSignedIn = async () => { - const signInChecks = await Promise.all( - providers.map(provider => { - return provider.identity - ? provider.api.getIdToken({ optional: true }) - : provider.api.getAccessToken('', { optional: true }); - }), - ); +type OIDCProviderSidebarProps = { + title: string; + icon: any; + apiRef: ApiRef; +}; - signInChecks.map((result, i) => { - providers[i].isSignedIn = !!result; - }); - - setProviders(providers); - }; - - // Any sign-in/sign-out activity on any provider is observed here - const observeProviderState = () => { - providers.map((provider, index) => { - provider.api.session$().subscribe((signedInState: boolean) => { - const mutatedProvider = providers[index]; - mutatedProvider.isSignedIn = signedInState; - providers[index] = mutatedProvider; - setProviders(providers.slice()); - }); - }); - }; +const OAuthProviderSidebarComponent: FC = ({ + title, + icon, + apiRef, +}) => { + const api = useApi(apiRef); + const [signedIn, setSignedIn] = useState(false); useEffect(() => { - setIsSignedIn(); - observeProviderState(); - }, [setIsSignedIn, observeProviderState]); + const checkSession = async () => { + const session = await api.getAccessToken('', { optional: true }); + setSignedIn(!!session); + }; - return providers; + const observeSession = () => { + api.session$().subscribe((signedInState: boolean) => { + if (signedIn !== signedInState) { + setSignedIn(signedInState); + } + }); + }; + + checkSession(); + observeSession(); + }, []); + + return ( + + ); +}; + +const OIDCProviderSidebarComponent: FC = ({ + title, + icon, + apiRef, +}) => { + const api = useApi(apiRef); + const [signedIn, setSignedIn] = useState(false); + + useEffect(() => { + const checkSession = async () => { + const session = await api.getIdToken({ optional: true }); + setSignedIn(!!session); + }; + + const observeSession = () => { + api.session$().subscribe((signedInState: boolean) => { + if (signedIn !== signedInState) { + setSignedIn(signedInState); + } + }); + }; + + checkSession(); + observeSession(); + }, []); + + return ( + + ); +}; + +const ProviderSidebarItemComponent: FC<{ + title: string; + icon: any; + signedIn: boolean; + api: OAuthApi | OpenIdConnectApi; + signInHandler: Function; +}> = ({ title, icon, signedIn, api, signInHandler }) => { + return ( + + (signedIn ? api.logout() : signInHandler())}> + + + + + + ); }; const useStyles = makeStyles({ @@ -108,38 +163,35 @@ const useStyles = makeStyles({ }, }); -export function SidebarUserSettings() { - const { isOpen: sidebarOpen } = useContext(SidebarContext); - const [open, setOpen] = React.useState(false); - const ref = useRef(); // for scrolling down when collapse item opens - const providers = useProviders(); +export const SidebarUserProfile: FC<{ setOpen: Function }> = ({ setOpen }) => { const [profile, setProfile] = useState(); + const ref = useRef(); // for scrolling down when collapse item opens + const googleAuth = useApi(googleAuthApiRef); const classes = useStyles(); - useEffect(() => { - const identityProvider = providers.find( - (provider: Provider) => provider.identity, - ); - if (identityProvider?.isSignedIn) { - identityProvider?.api - .getProfile({ optional: true }) - .then((userProfile: ProfileInfo) => { - setProfile(userProfile); - }); - } else { - setProfile(undefined); - } - }, [providers, open]); - const handleClick = () => { setOpen(!open); setTimeout(() => ref.current?.scrollIntoView({ behavior: 'smooth' }), 300); }; - // Close the provider list when sidebar collapse useEffect(() => { - if (!sidebarOpen && open) setOpen(false); - }, [open, sidebarOpen]); + const fetchProfile = async () => { + await googleAuth + .getProfile({ optional: true }) + .then((userProfile?: ProfileInfo) => { + setProfile(userProfile); + }); + }; + + const observeSession = () => { + googleAuth.session$().subscribe(() => { + fetchProfile(); + }); + }; + + fetchProfile(); + observeSession(); + }, [open]); // Handle main auth info that is shown on the collapsible SidebarItem let avatar; @@ -170,37 +222,33 @@ export function SidebarUserSettings() { > {open ? : } + + ); +}; + +export function SidebarUserSettings() { + const { isOpen: sidebarOpen } = useContext(SidebarContext); + const [open, setOpen] = React.useState(false); + + // Close the provider list when sidebar collapse + useEffect(() => { + if (!sidebarOpen && open) setOpen(false); + }, [open, sidebarOpen]); + + return ( + <> + - {providers.map((provider: Provider) => ( - - - provider.isSignedIn - ? provider.api.logout() - : provider.api.getAccessToken() - } - > - - - - - - ))} + + );