import React , { FC , useCallback , useEffect , useMemo , useReducer } from 'react' ; import { Route , Switch , useHistory } from 'react-router-dom' ; import type { History } from 'history' ; import type { User } from 'oidc-client' ; import OidcSignIn from './OidcSignIn' ; import OidcSilentSignIn from './OidcSilentSignIn' ; import { getAuthSettings , getUserManager } from '../userManager' ; import { ActionType , initialState , reducer } from './Authenticator.state' ; import { AuthContext } from './AuthContext' ; import { CheckAuth } from './CheckAuth' ; import { AuthSettings } from '../AuthSettings' ; interface Props { /**
     * If true, Authenticator automatically starts login flow and does not render children until user is fully logged in.
     * If false, children are responsible for rendering a login button and loading indicator.
     * Default: true
     */ autoLogin ? : boolean ; } const getReturnUrl = ( history , { redirectPath } ) => { const returnUrl = history . location . pathname + history . location . search ; if ( redirectPath && returnUrl . startsWith ( redirectPath ) ) { return '/' ; } return returnUrl ; } ; export const Authenticator = ( { children , autoLogin = true } ) => { const history = useHistory ( ) ; const reducerInstance = useReducer ( reducer , initialState ) ; const [ state , dispatch ] = reducerInstance ; const userManager = getUserManager ( ) ; const authSettings = getAuthSettings ( ) ; const signIn = useCallback ( async function signIn ( ) { dispatch ( { type : ActionType . SIGNIN_START , } ) ; return userManager . signinRedirect ( { state : getReturnUrl ( history , authSettings ) , } ) ; } , [ dispatch , userManager , authSettings , history ] ) ; const signInSilent = useCallback ( async function signInSilent ( ) { let user = null ; dispatch ( { type : ActionType . SIGNIN_START , } ) ; try { user = await userManager . signinSilent ( ) ; dispatch ( { type : ActionType . SIGNIN_SUCCESS , payload : user } ) ; } catch ( error ) { console . error ( 'Authenticator: Silent signin failed' , error ) ; dispatch ( { type : ActionType . SIGNIN_FAILURE } ) ; } return user ; } , [ userManager , dispatch ] ) ; const switchUser = useCallback ( async function switchUser ( nationalId ? ) { const args = nationalId !== undefined ? { login_hint : nationalId , /**
                 * TODO: remove this.
                 * It is currently required to switch delegations, but we'd like
                 * the IDS to handle login_required and other potential road
                 * blocks. Now OidcSignIn is handling login_required.
                 */ prompt : 'none' , } : { login_hint : 'delegations' , } ; dispatch ( { type : ActionType . SWITCH_USER , } ) ; return userManager . signinRedirect ( { state : authSettings . switchUserRedirectUrl ?? getReturnUrl ( history , authSettings ) , ... args , } ) ; } , [ userManager , dispatch , history , authSettings ] ) ; const signOut = useCallback ( async function signOut ( ) { dispatch ( { type : ActionType . LOGGING_OUT , } ) ; await userManager . signoutRedirect ( ) ; } , [ userManager , dispatch ] ) ; const checkLogin = useCallback ( async function checkLogin ( ) { dispatch ( { type : ActionType . SIGNIN_START , } ) ; const storedUser = await userManager . getUser ( ) ; if ( storedUser && ! storedUser . expired ) { dispatch ( { type : ActionType . SIGNIN_SUCCESS , payload : storedUser , } ) ; } else if ( autoLogin ) { if ( storedUser && ( await signInSilent ( ) ) ) { return ; } await signIn ( ) ; } else { await signInSilent ( ) ; } } , [ userManager , dispatch , signIn , signInSilent , autoLogin ] ) ; useEffect ( ( ) => { if ( state . userInfo === null ) { return ; } const userLoaded = ( user ) => { dispatch ( { type : ActionType . USER_LOADED , payload : user , } ) ; } ; const userSignedOut = async ( ) => { dispatch ( { type : ActionType . LOGGED_OUT , } ) ; await userManager . removeUser ( ) ; if ( autoLogin ) { signIn ( ) ; } } ; userManager . events . addUserLoaded ( userLoaded ) ; userManager . events . addUserSignedOut ( userSignedOut ) ; return ( ) => { userManager . events . removeUserLoaded ( userLoaded ) ; userManager . events . removeUserSignedOut ( userSignedOut ) ; } ; } , [ dispatch , userManager , signIn , autoLogin , state . userInfo === null ] ) ; const context = useMemo ( ( ) => ( { ... state , signIn , signInSilent , switchUser , signOut , } ) , [ state , signIn , signInSilent , switchUser , signOut ] ) ; return ( < AuthContext . Provider value = { context } > < Switch > < Route exact path = { authSettings . redirectPath } render = { ( ) => < OidcSignIn authDispatch = { dispatch } />}/ > < Route exact path = { authSettings . redirectPathSilent } component = { OidcSilentSignIn } / > < Route > < CheckAuth checkLogin = { checkLogin } autoLogin = { autoLogin } > { children } < /CheckAuth> < /Route> < /Switch> < /AuthContext.Provider>); } ;