bjerkio/oidc-react

Silent Sign-In causes Loop

arnediekmann opened this issue · 2 comments

Firstly, thanks a lot for your package and all the work that went into it. We are using it at the moment to implement single-sign-on in conjunction with a KeyCloak server. We want to integrate a silent sign-in, but are running into an issue. The OIDC config looks like the following:

const oidcConfig = {
  authority: "<url>",
  clientId: "<clientId>",
  responseType: "code",
  scope: "openid",
  redirectUri: "http://localhost:3000",
  autoSignIn: false,
};

The login and logout work flawlessly with that configuration. However, the following component will cause a loop of requests to the KeyCloak server if the user is not logged in before finally stopping when a request runs into a timeout. The code of the component looks as follows:

import React, { useEffect } from "react";
import { useAuth } from "oidc-react";

const LoggedIn = () => {
  const auth = useAuth();

  useEffect(() => {
    const authenticate = async () => {
      if (!auth.userData) {
        try {
          await auth.userManager.signinSilent();
        } catch (e) {
          console.error(e);
        }
      }
    };

    authenticate();
  }, []);

  if (auth.userData) {
    return <div>Logged in!</div>;
  }
  return <div>Not logged in!</div>;
};

export default LoggedIn;

Which will cause the following cascade of requests in the IFrame the package creates:

Screenshot 2022-11-18 at 08 32 20

The console logs:

LoggedIn.js:13 ErrorTimeout: IFrame timed out without a response
    at http://localhost:3000/static/js/bundle.js:11242:54
authenticate @ LoggedIn.js:13
await in authenticate (async)
(anonymous) @ LoggedIn.js:18
commitHookEffectListMount @ react-dom.development.js:23150
commitPassiveMountOnFiber @ react-dom.development.js:24926
commitPassiveMountEffects_complete @ react-dom.development.js:24891
commitPassiveMountEffects_begin @ react-dom.development.js:24878
commitPassiveMountEffects @ react-dom.development.js:24866
flushPassiveEffectsImpl @ react-dom.development.js:27039
flushPassiveEffects @ react-dom.development.js:26984
(anonymous) @ react-dom.development.js:26769
workLoop @ scheduler.development.js:266
flushWork @ scheduler.development.js:239
performWorkUntilDeadline @ scheduler.development.js:533
LoggedIn.js:13 TypeError: Failed to fetch
    at JsonService.fetchWithTimeout (JsonService.ts:50:1)
    at JsonService.getJson (JsonService.ts:89:1)
    at MetadataService.getMetadata (MetadataService.ts:52:1)
    at MetadataService._getMetadataProperty (MetadataService.ts:100:1)
    at MetadataService.getAuthorizationEndpoint (MetadataService.ts:64:1)
    at OidcClient.createSigninRequest (OidcClient.ts:113:1)
    at UserManager._signinStart (UserManager.ts:386:1)
    at UserManager._signin (UserManager.ts:379:1)
    at UserManager.signinSilent (UserManager.ts:246:1)
    at async authenticate (VM3354 bundle.js:201:11)
[...]

The individual redirects from the KeyCloak server all the form http://localhost:3000?error=login_required&state=<state-that-was-sent> which look to me correct, though I don't have too much experience with OIDC to say for sure.

I have a few questions concerning this behavior:

  1. Is the KeyCloak server seemingly behaving correctly?
  2. Is this an error that you have seen in the past?
  3. Can you see something in the code snippet that might be wrong and causing this issue?
  4. Can this be related to the browser that is used?

If one of the maintainers or anyone in the community can take a look at this, this would be much appreciated! 🙂

This is very old, but I figured I'd drop in what I do in this scenario:

Basically, the KeyCloak server in your screenshots appears to be responding with an error of login_required which implies you cannot do a silent sign-in. A silent sign-in works if you're already authenticated, or have a token that hasn't expired to allow you to perform sign-in again. So, what I do in a very similar looking component is the following:

try {
   await auth.userManager.signinSilent();
} catch (e) {
  if (e.error === 'login_required') {
    await auth.signIn();
  }
}

I also technically do a few steps before calling auth.signIn() to save things like where the user was navigating to, but that doesn't really matter for the specific issue at hand.

Hey @pseudoramble, thanks for your reply! You are exactly right, that I am not authenticated when I get the errors. However, I would think, that this error should only occur once 🤔

We have since I opened this issue swapped the frameworks, thus I will close this. But thank you for your information and hints here!