react-oidc-context
react-oidc-context copied to clipboard
Multiple instances?
Thanks for the great library, took me some time to stumble across it but looks to be exactly what I need.
There doesn't appear to be support for multiple instances of the OIDC client, where a single SPA may need to authenticate against multiple different identity servers at the same time - do you have any ideas for how this could be worked around?
Similar to how https://github.com/AxaGuilDEv/react-oidc manages this: multiple configurations can be applied and then the hook can refer to a configuration to pull out a specific client instance.
Untested idea: You could add per identity server one auth provider, nested. Then by using https://github.com/authts/react-oidc-context/blob/fd24a701a16c93d051bf25a82ac1a85dffb4f7a0/src/AuthProvider.tsx#L35-L46 you must makes sure that the correct auth provider interacts when needed....
Yeah, so far as I know there's no way to retrieve a parent if there are multiple nested providers of the same type - React overrides it and gives you the nearest provider.
Well, got it working with the most terrible of PoC code, for anyone that's curious: basically, store the higher-level provider in a secondary context for later retrieval (in my case, the aptly-named StupidContext.
Top-level:
type WrapperProviderProps = {
children: React.ReactElement;
};
function InternalContextSave(props: WrapperProviderProps) {
const auth = useAuth();
return (
<StupidContext.Provider value={auth}>
{props.children}
</StupidContext.Provider>
);
}
function WrapperProvider(props: WrapperProviderProps) {
return (
<AuthProvider
{...{
authority: `${getEnvironmentVariable(
'VITE_KEYCLOAK_URL',
)}/realms/${getEnvironmentVariable(
'VITE_KEYCLOAK_SECOND_REALM',
)}`,
client_id: getEnvironmentVariable(
'VITE_KEYCLOAK_SECOND_CLIENT_ID',
),
redirect_uri: 'http://localhost:3000/second',
silent_redirect_uri: 'http://localhost:3000/second',
}}
skipSigninCallback={window.location.pathname !== '/second'}
onSigninCallback={(user) => {
window.history.replaceState(
{},
document.title,
window.location.pathname,
);
}}
loadUserInfo
>
<InternalContextSave>{props.children}</InternalContextSave>
</AuthProvider>
);
}
ReactDOM.render(
<React.StrictMode>
<WrapperProvider>
<AuthProvider
{...{
authority: `${getEnvironmentVariable(
'VITE_KEYCLOAK_URL',
)}/realms/${getEnvironmentVariable('VITE_KEYCLOAK_REALM')}`,
client_id: getEnvironmentVariable(
'VITE_KEYCLOAK_CLIENT_ID',
),
redirect_uri: 'http://localhost:3000/first',
silent_redirect_uri: 'http://localhost:3000/first',
}}
skipSigninCallback={window.location.pathname !== '/first'}
onSigninCallback={(user) => {
window.history.replaceState(
{},
document.title,
window.location.pathname,
);
}}
loadUserInfo
// automaticSilentRenew
>
<App />
</AuthProvider>
</WrapperProvider>
</React.StrictMode>,
document.getElementById('root'),
);
Less high-level:
useEffect(() => {
if (!isLoading && !isAuthenticated) {
window.sessionStorage.setItem(
'redirectTo',
`${location.pathname}${location.search}`,
);
signinRedirect();
}
}, [isLoading, isAuthenticated]);
const secondAuth = useStupid();
useEffect(() => {
if (!secondAuth?.isLoading && !secondAuth?.isAuthenticated) {
secondAuth?.signinRedirect();
}
}, [secondAuth?.isLoading, secondAuth?.isAuthenticated]);
Thanks for the idea!
Thanks for sharing the solution!
I don't think this has a very satisfying solution yet; we can keep this open.
The real issue is that we don't expose a way to obtain a ref to the auth context without a child component using the hook. Ideally, something like this should be possible:
import React, { createContext, useCallback, useContext } from 'react'
import { AuthProvider } from 'react-oidc-context';
const multiAuthContext = createContext(new Map());
function useMultiAuth(authority) {
const auth = useContext(multiAuthContext).get(authority);
if (!auth) throw new Error(`No authority configuration for ${authority}`);
return auth;
}
function MyComponent() {
const auth = useMultiAuth("https://authority1");
useEffect(() => {
auth.signinRedirect();
}, []);
}
function App() {
const authoritiesRef = useRef(new Map());
const registerAuthority = useCallback(auth => authorities.set(auth.settings.authority, auth));
return (
<multiAuthContext.Provider value={authoritiesRef.current}>
<AuthProvider ref={registerAuthority} authority="https://authority1" client_id="clientId1" />
<AuthProvider ref={registerAuthority} authority="https://authority2" client_id="clientId2" />
<MyComponent />
</multiAuthContext.Provider>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
);
Bumping this thread: the possibility of changing authority conditionally dynamically would be very useful because for example keycloak has the notion of realms and it is a normal use case to change authority, otherwise user needs to save realm in localStorage or a cookie or sessionStorage.
Bumping this thread: the possibility of changing authority conditionally dynamically would be very useful because for example keycloak has the notion of realms and it is a normal use case to change authority, otherwise user needs to save realm in localStorage or a cookie or sessionStorage.
A better example would be integration with third party apps using oauth such as jira, in rare cases you would need several oidc client instances for the same keycloak instance. I will make a poc myself and see if I get a better solution
Nice @satanshiro looking forward!
hi
unfortunately I can't test locally,
I would like to try and do the following change to AuthProvider.tsx:
return ( context ? ( <context.Provider value={{ ...state, ...userManagerContext, removeUser, signoutRedirect, signoutPopup, }} > {children} </context.Provider>): (<AuthContext.Provider value={{ ...state, ...userManagerContext, removeUser, signoutRedirect, signoutPopup, }} > {children} </AuthContext.Provider>) );
meaning I would like to be able to manually inject different auth context and test so users would only need to recreate useAuth and authcontext with a different name and as such 2 contexts should be different instances.
npm pack does not work locally and npm link gives me other peer dependencies error.
`npm pack
[email protected] prepack npm run build
[email protected] build node scripts/build.js && npm run build-types
[email protected] build-types tsc --emitDeclarationOnly && api-extractor run
api-extractor 7.32.0 - https://api-extractor.com/
Using configuration from ./api-extractor.json Analysis will use the bundled TypeScript version 4.8.4 Warning: You have changed the public API signature for this project. Please copy the file "temp/react-oidc-context.api.md" to "docs/react-oidc-context.api.md", or perform a local build (which does this automatically). See the Git repo documentation for more info.
API Extractor completed with warnings npm ERR! code 1 npm ERR! path /home/oren/development/react-oidc-context npm ERR! command failed npm ERR! command sh -c -- npm run build
npm ERR! A complete log of this run can be found in: npm ERR! /home/oren/.npm/_logs/2022-10-11T13_22_44_739Z-debug-0.log` in any case it is an important feature for when users stop using api keys for remote api access and want an auth flow instead. does anyone know what I am missing to run npm pack locally?
Anyone have ideas on how to solve this issue for now? Really appreciate any input!