import React, {useContext, useEffect, useState} from 'react'
import {AuthenticationProvider, useReactOidc as axaReactOidc} from '@axa-fr/react-oidc-context';
import {ConfigContext} from "./ConfigManager";
import {ApiManager} from "./ApiManager";
import {Loading, NeedLogin, NoAccess} from "../components/extras";
import {logger} from "./SparkLogger";
import JcuIdentityProvider from "./JcuIdentityProvider";
type oidcConfiguration = {
client_id: string,
redirect_uri: string,
response_type: string,
scope: string,
authority: string,
silent_redirect_uri: string,
automaticSilentRenew?: boolean,
loadUserInfo?: boolean,
post_logout_redirect_uri?: string,
metadata?: oidcMetadata,
filterProtocolClaims?: boolean,
}
type oidcMetadata = {
issuer?: string,
jwks_uri?: string,
authorization_endpoint?: string,
token_endpoint?: string,
userinfo_endpoint?: string,
end_session_endpoint?: string,
revocation_endpoint?: string,
introspection_endpoint?: string,
}
type providerProps = {
notAuthenticated?: React.Component, // react component displayed during authentication
notAuthorized?: React.Component, // react component displayed in case user is not Authorised
authenticating?: React.Component, // react component displayed when about to redirect user to be authenticated
callbackComponentOverride?: React.Component, // react component displayed when user is connected
sessionLostComponent?: React.Component,
configuration: oidcConfiguration,
isEnabled?: boolean,
loggerLevel?: number
}
// List of oidc configuration keys that will need to be rebased
const REBASEABLE_URLS = [
'redirect_uri',
'silent_redirect_uri',
'post_logout_redirect_uri'
]
/**
* Used to set up generic URLs (e.g. /callback) into their app specific equivalent
* (e.g. https://apps.jcu.edu.au/password-selfservice/callback)
* @ignore
*
* @param oidcConfig The OIDC config containing the generic urls
* @param baseUrl The base URL for the SPA
*
* @returns An object containing the oidc with updated rebase-able values
*/
function rebaseUrls(oidcConfig: oidcConfiguration, baseUrl) {
let newConfig = {...oidcConfig}
for (let key of REBASEABLE_URLS) {
if (newConfig[key] && newConfig[key].indexOf('/') === 0) {
newConfig[key] = baseUrl + newConfig[key]
}
}
return newConfig
}
// JCU defaults using SIT SSO and testapp ID
const DEFAULT_OIDC_CONFIG: oidcConfiguration = {
authority: 'https://sit-sso.jcu.edu.au/openam/oauth2/jcu',
silent_redirect_uri: '/silent_callback',
client_id: 'testapp',
redirect_uri: '/redircallback',
post_logout_redirect_uri: '/',
response_type: 'code',
scope: 'openid profile email',
filterProtocolClaims: true,
loadUserInfo: true,
automaticSilentRenew: true
}
/**
*
* @component
* @category Context Providers
*/
function AxaOidcProvider(props) {
// Load the application configuration
const appConfigContext = useContext(ConfigContext)
const [oidcConfig, setOidcConfig] = useState<oidcConfiguration | null>(null)
useEffect(() => {
if (appConfigContext) {
let requestedScopes = []
// Loop through all the APIs registered in the API manager, get their scopes, and
// template them if required (to get the correct scope for this environment)
for (let i of ApiManager.interfacesToLoad) {
let scopes = Object.values(i.scopes)
if (scopes) {
for (let scope of scopes) {
//@ts-ignore
requestedScopes.push(ApiManager.getTemplatedScope(scope as string, appConfigContext.env))
}
}
}
// Combine all the scopes into an OIDC scope string
let scopeString = requestedScopes.join(' ')
// Determine the base URL for this application, to ensure all callback routes go to the correct application
let baseUrl =
`${window.location.protocol}//${window.location.hostname}${
window.location.port ? `:${window.location.port}` : ''
}` + appConfigContext.basePath
// Merge the default OIDC configuration and the app specific configuration.
// App specific overrides default
let {scope: configScopes, ...authConfigNoScope} = appConfigContext.authentication
let oidcConfig = {...DEFAULT_OIDC_CONFIG, ...authConfigNoScope}
// Attach the API and app specific scopes to the base list provided
oidcConfig.scope = [oidcConfig.scope.trim(), scopeString, configScopes].join(" ")
// Clean the oidc scope string to be compliant
oidcConfig.scope = oidcConfig.scope.replace(/\s+/g, " ")
// Convert any generic OIDC callbacks (e.g. /callback) into their app specific equivalent
// (e.g. https://localhost:3000/callback)
oidcConfig = rebaseUrls(oidcConfig, baseUrl)
// Save the OIDC configuration into state
setOidcConfig(oidcConfig)
}
// When component unmounts, set OIDC config back to null
return () => setOidcConfig(null)
}, [appConfigContext])
// If there's no config loading the provider will crash, instead render null
if (!oidcConfig) {
return null
} else {
return (
<AuthenticationProvider
// @ts-ignore
configuration={oidcConfig}
// @ts-ignore
notAuthenticated={NeedLogin}
authenticating={Loading}
callbackComponentOverride={Loading}
notAuthorized={NoAccess}
//loggerLevel={oidcLog.DEBUG}
>
<JcuIdentityProvider oidcAuthority={oidcConfig.authority}>
{props.children}
</JcuIdentityProvider>
</AuthenticationProvider>
)
}
}
function useReactOidc() {
logger.devWarn("useReactOidc is deprecated. useOidc is the preferred hook to use.")
return axaReactOidc()
}
export default AxaOidcProvider
//TODO: Remove old useReactOidc calls, use useOidc. Likely breaking change
export {useReactOidc, AxaOidcProvider}
Source