authts / oidc-client-ts

OpenID Connect (OIDC) and OAuth2 protocol support for browser-based JavaScript applications
https://authts.github.io/oidc-client-ts/
Apache License 2.0
1.28k stars 191 forks source link

Infinite Redirects before Redirecting to Identity Server Login Page #1296

Open ajmaln-tw opened 7 months ago

ajmaln-tw commented 7 months ago

When attempting to implement a redirect to the Identity Server login page, the application appears to be stuck in an infinite redirection loop. I suspect this may be due to calling the handleLogin function within the component. The expected behavior is to redirect to the Identity Server login page upon entering the web app URL (http://localhost:4200/), and after successful login, redirect back to http://localhost:4200/. Additionally, if a session is already present, the app should not redirect to the login page but should display http://localhost:4200/.

here is my code

configuration of oidc client

export const oidcClientConfig = {
  authority: "https://my-domain/IdentityServer",
  client_id: "web",
  redirect_uri: getUrl(),
  client_secret: "my-secret",
  response_type: "code",
  scope: "openid profile api1.read offline_access"
};

Inside the App initiated the user manager and pass as props to the ProjectLayout Component

  const userManager = new UserManager({
    ...oidcClientConfig, userStore: new WebStorageStateStore({
      store: window.localStorage
    })
  });

Project layout where OIDC authentication happens


const ProjectLayout = ({ children, userManager }) => {

    const dispatch = useDispatch();
    const handleLogin = async () => {
    const user = await userManager.signinRedirect();
    localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, JSON.parse(user));

    };
    const handleLogout = async () => {
        dispatch(commonSliceActions.setAuth(false));
        localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN);
        await userManager.signoutRedirect();

    };

handleLogin()
    return (
        <Box
            sx={{ minHeight: "100vh", width: 1 }}
        >
            <Header handleLogout={handleLogout} />
            <Box sx={{ display: "flex", justifyContent: "space-between ", height: "100%" }}>
                {/* <SideBar /> */}
                <Box className="mainTreemetry" sx={{ flexGrow: 1, overflowX: "auto", display: "flex", flexDirection: "column", justifyContent: "space-between", height: "calc(100vh - 60px) !important ", width: "100%", overflowY: "auto" }}>
                    <Box

                        sx={{ bgcolor: "white.main", borderRadius: "20px", flexGrow: 1 }}
                    >
                        {children}
                    </Box>
                    <Footer />
                </Box>
            </Box>
        </Box >
    );
};

export default ProjectLayout;

Steps to Reproduce:

  1. Access the web app using the URL: http://localhost:4200/.
  2. If no session is available, the app correctly redirects to the Identity Server login page.
  3. After successful login, the app again calls the handleLogin function, resulting in an infinite redirection loop.

Expected Behavior:

  1. On entering http://localhost:4200/, the app should redirect to the Identity Server login page if no session is available.
  2. After successful login, it should redirect back to http://localhost:4200/.
  3. If a session is already present, the app should not redirect to the login page but display http://localhost:4200/.

Actual Behavior: The application is stuck in an infinite redirection loop.

screenshots github issues

pamapa commented 7 months ago

Its most probably a logic bug in you application code. Maybe enabling logging helps you to find out: https://authts.github.io/oidc-client-ts/#md:logging

gary-archer commented 5 months ago

This needs addressing in the application logic. A couple of techniques that help to avoid redirect loops:

WHEN TO REDIRECT

Only trigger an authorization redirect if you don't have an access token yet or if access token refresh fails.

Don't trigger an authorization redirect if you get a 401 from an API.

If you get a permanent 401 (after a successful token refresh) it is a sign that the token configuration is wrong. A new authorization redirect won't fix that.

TIME BASED PROTECTION

One technique you can use is to record the time for the last successful login, then use it to prevent a new login:

private async _preventRedirectLoop(api401Error: UIError): Promise<void> {

    const currentTime = new Date().getTime();
    const millisecondsSinceLogin = currentTime - this._loginTime;
    if (millisecondsSinceLogin < 1000) {
            await this.clearLoginState();
            throw api401Error;
    }
}

One place where this is useful is when iframe based renewal fails due to the (third-party) SSO cookie being dropped. For example, this happens in the Safari browser and can be another cause for a redirect loop.

EXAMPLE OF MINE

Any OAuth client needs to write some reliability code to deal with expiry events. You can run my oidc client example or look at its code to get some ideas.