react-oidc-context
react-oidc-context copied to clipboard
unclear how to sign out without hooks
Our project was using AppAuth-JS library before discovering this. It is much more succinct and sensible so thank you.
One of the challenges faced in using this library: we are using Apollo client to make GraphQL requests and we use a link to detect 401 errors. From the link, it's not clear how one could trigger a signout redirect. Thus, as a workaround, a boolean flag needsSignoutRedirect was created in our redux store which gets picked up by a React hook which performs the signout.
How can one halt all rendering while the page redirects though? If the network error reaches the query hook, the page will display an error message momentarily. Throwing an error in the link and catching it in the error boundary in order to halt the rendering cycles could prevent the hook from catching the needsSignoutRedirect flag.
Could you state what functionality seems to be missing from this library more clearly? I'm having trouble understanding your problem, especially around
we use a link to detect 401 errors
@kherock on 401, I need to ensure session was ended on the backend so I have to call auth.signinRedirect but I don't have access to hooks in the apollo link. Here's a reference for apollo link error handler: https://www.apollographql.com/docs/react/data/error-handling/#on-graphql-errors
ah I see. I'm not familiar with Apollo Link but it sounds like you want to perform the sign out redirect from outside of the context of your React app?
Would creating a standalone UserManager instance from oidc-client-ts directly solve your problem?
Hmm, thanks @kherock, I will try creating my own UserManager and passing it to the implementation prop. However, after looking into this further, I think this library might benefit from an "imperative handle" as described in this related SO issue: https://stackoverflow.com/questions/59232118/how-to-acess-react-context-from-apollo-set-context-http-link
Just an FYI, implementation takes in a class instance, this library doesn't support passing an external usermanager instance.
What I meant in my suggestion was in response to
I have to call auth.signinRedirect but I don't have access to hooks in the apollo link
I may have misunderstood and you actually just meant that you're in a server context in Link. If you need to do OIDC client things (such as redirecting to the IDP) from outside of any React context on the client side, you should use oidc-client-ts directly in those contexts.
I think this example is good argument for allowing external usermanager instances to be passed as a prop to the provider. @pamapa?
In researching the issue of not being able to access context values from the Apollo link, I discovered this: https://stackoverflow.com/questions/59232118/how-to-acess-react-context-from-apollo-set-context-http-link . It sound like this library could use an imperative handle to the context in order to enable this functionality. I think it would be a better approach than passing UserManager instance because the user could get access to the whole api of the hook.
It's not very clear how to use the oidc-client-ts library in order to perform a signout redirect. Do I have to create another UserManager? Is it going to be compatible with this library's context UserManager e.g. if I sign in with react-oidc-context can I sign out with a newly constructed UserManager from oidc-client-ts?
You would have to create another UserManager, but I think providing an easy way of obtaining a handle to the context without creating a child component is the better solution here. UserManager instances don't talk to each other, so some things would fall out of sync. This is kind of related to #292.
If you have time to open a PR adding an imperative handle to AuthProvider, I'd appreciate it!
@kherock I was able to confirm instantiating another UserManager using the same config as the provider and calling it's signoutRedirect seemed to work as expected. I will see if I can dedicate some company bandwidth for a PR.
@kherock do you think you could speak more to "some things would fall out of sync"? I'm wondering what the potential issues could be for JUST signoutRedirect. I read here in OIDC, if prompt is none, an id token hint is needed for refresh but I'm not sure if it's needed for signout and I found this document a bit confusing/vague.
Actually, I take that back. Two user managers should be able to operate next to each other as long as they aren't racing with each other for certain global resources. I think the only instances where this may come up is when waiting for a popup window or silent iframe to respond.
I added a separate issue for passing an UserManager into: #490
For those still struggling with this I have this issue trying to create a method that run the keycloak's sign out in the axios response interceptor. Here's how I did it:
I've create a file called UserManagerClient.ts
import {
UserManager,
UserManagerSettings,
WebStorageStateStore,
} from 'oidc-client-ts'
const userConfig: UserManagerSettings = {
authority: 'authority_here',
client_id: 'client_here',
redirect_uri: 'http://localhost:3000'
}
const userManager = new UserManager(userConfig)
export default userManager
In my App.tsx
import { AuthProvider, hasAuthParams, useAuth } from 'react-oidc-context'
import UserManagerClient from './UserManagerClient'
const oidcConfig = {
userManager:
UserManagerClient,
onSigninCallback: (): void => {
window.history.replaceState({}, document.title, window.location.pathname)
},
}
const App = () => (
<AuthProvider {...oidcConfig}>
<AppRouter/>
</AuthProvider>
)
export default App
In my axios interceptor:
import UserManagerClient from './UserManagerClient'
axios.interceptors.response.use(
(response) => {
return response
},
async (error) => {
if (error?.response?.status === 401) {
const user = await UserManagerClient.getUser()
UserManagerClient.signoutRedirect({
post_logout_redirect_uri: window.location.href,
id_token_hint: user?.id_token,
})
}
}
)