authts / react-oidc-context

Lightweight auth library based on oidc-client-ts for React single page applications (SPA). Support for hooks and higher-order components (HOC).
MIT License
681 stars 65 forks source link

After logging in, the browser back button redirects users to a malformed URL with OnRedirecting component displayed forever #1386

Open cfecherolle opened 3 hours ago

cfecherolle commented 3 hours ago

Hi,

I've been having headaches trying to fix a bug I'm encountering when using react-oidc-context to authenticate with my Keycloak server.

Bug scenario

  • Log in with username and password.
  • After being successfully redirected to the redirect URI, use the browser back button once.

Expected result

  • User is redirected to the redirect URI again (as they are already logged in)

Actual result

  • The user is redirected to a URL of the form :
    https://myapp.com/?state=88db7655520042d6a0b64e55895de911&session_state=99a1baa0-0637-4bc0-8c79-0ed229656e7a&code=adff040d-614e-476e-95a7-a08daded08ad.99a1baa0-0637-4bc0-8c79-0ed229656e7a.9e7b4e82-0244-42dd-8130-3dc2be1e2f23

    And the OnRedirecting component configured using withAuthenticationRequired is displayed forever.

Relevant info and configuration: My main component is configured like this:

export default withAuthenticationRequired(connectedRouter, {
    onBeforeSignin: () => {
        sessionStorage.setItem(AUTH_REDIRECT_URI_STORAGE_KEY, window.location.pathname);
    },
    OnRedirecting: () => <FullPageSpinner/>,
});

The whole React app is wrapped with:

<AuthProvider userManager={userManager} onSigninCallback={onSignInCallback}>

With props imported from:

import {UserManager, WebStorageStateStore} from "oidc-client-ts";

export const keycloakAuthority = `https://myapp.com/auth/realms/${import.meta.env.VITE_KEYCLOAK_REALM}`;
export const keycloakBackendClientId = "backend";

export const userManager = new UserManager({
    authority: keycloakAuthority,
    client_id: keycloakBackendClientId,
    monitorSession: true,
    redirect_uri: import.meta.env.VITE_KEYCLOAK_REDIRECT_URI,
    revokeTokensOnSignout: true,
    userStore: new WebStorageStateStore({store: window.sessionStorage}),
});

export const onSignInCallback = () => {
    const redirectPathname = sessionStorage.getItem(AUTH_REDIRECT_URI_STORAGE_KEY) || window.location.pathname;
    const redirectUrl = import.meta.env.VITE_KEYCLOAK_REDIRECT_URI + redirectPathname;
    window.location.replace(redirectUrl);
};

AUTH_REDIRECT_URI_STORAGE_KEY is the key of a session storage item I'm using to redirect users to their targeted URL after logging in.

The window.location.replace call in my sign in callback works in a way, as it redirects users to the redirect URI without any extraneous query params after logging in. Yet, when using the browser back button, this state is still present in history. I wish there was a way to either skip it (not adding it to history) or remove it after.

I would love to keep using this library as it was fast and easy to configure but if I can't get around this issue, this seems like a deal breaker for client-facing web apps. Any suggestions will be greatly appreciated 🙏

cfecherolle commented 3 hours ago

I found this stale issue which seems to be related: https://github.com/authts/react-oidc-context/issues/683