oidc-client icon indicating copy to clipboard operation
oidc-client copied to clipboard

useOidcUser briefly returning null on page navigation refresh during authenticated session [NextJs]

Open kramer99 opened this issue 10 months ago • 13 comments

Issue and Steps to Reproduce

Create a starter NextJs app (choose "no" to App router, "yes" tosrc/):

npx create-next-app

Modify src/pages/index.tsx to:

import { OidcSecure, useOidcUser } from "@axa-fr/react-oidc";

const isBrowser = () => typeof window !== "undefined";

export default function Home() {
  const { oidcUser } = useOidcUser();
  console.log("isBrowser: " + isBrowser() + ", oidcUser: ");
  console.log(oidcUser);

  return (
    <div>
      Anyone can see this
      <OidcSecure>
        <div className="text-red-400">
          Not everybody can see this.... {oidcUser?.email}
        </div>
      </OidcSecure>
    </div>
  );
}

Modify src/pages/_app.tsx:

import "@/styles/globals.css";
import { OidcConfiguration, OidcProvider } from "@axa-fr/react-oidc";
import type { AppProps } from "next/app";

const oidcConfig: OidcConfiguration = {
  authority: <snip>,
  client_id: <snip>,
  service_worker_only: false,
  scope: "openid profile email offline_access",
  redirect_uri: "http://localhost:3000/ibor/callback",
};

export default function App({ Component, pageProps }: AppProps) {
  return (
    <OidcProvider configuration={oidcConfig}>
      <Component {...pageProps} />
    </OidcProvider>
  );
}

Add a callback route src/pages/ibor/callback/index.tsx:

import React from "react";
import { useOidc } from "@axa-fr/react-oidc";
import { useRouter } from "next/router";

const Page = () => {
  const { isAuthenticated } = useOidc();
  const router = useRouter();

  if (isAuthenticated) {
    router.push("/");
  } else router.push("/signin");

  return <div>Loading...</div>;
};

export default Page;

Versions

7.22.3 (I also tried the alpha version mentioned here: https://github.com/AxaFrance/oidc-client/issues/1090, with the new config option, but had no success there)

Screenshots

Expected

useOidcUser always returns the user object when user is authenticated (and we're not in an SSR context)

Actual

After authenticating, you'll see:

image

...and in the console:

image

I had at first assumed the reason would be that useOidcUser was getting called during Server Side Rendering... but as you can see, that's not the cause (note the isBrowser check). So local / session storage should be available at that time.

If you refresh the page, or navigate to another route, you'll see the same thing.

The reason this matters is that if you're taking, say, the name or email address of the logged in user and displaying them in a header, they will flicker and disappear before reappearing whenever the user navigates / refreshes. There are workarounds (you can cache it in some state somewhere) - but that's obviously not ideal.

Additional Details

  • Installed packages:

kramer99 avatar Apr 19 '24 21:04 kramer99

Hi @kramer99 thank you for your issue and feedback. I will make more test to debug and enhance it !

guillaume-chervet avatar Apr 22 '24 14:04 guillaume-chervet

version v7.22.4-alpha.1425 should work better @kramer99 :)

guillaume-chervet avatar Apr 22 '24 19:04 guillaume-chervet

Confirmed, fix looks good on my side.

Thanks for the quick work @guillaume-chervet !

kramer99 avatar Apr 22 '24 21:04 kramer99

Sorry @guillaume-chervet - it looks like it was only a partial fix.

Behaviour is correct during page navigation and refresh, but there is still a problem when receiving the callback after authentication.

If you add isAuthenticated to the console.log in the example above, and then clear site data and re-authenticate, you will see this:

image

kramer99 avatar Apr 24 '24 21:04 kramer99

Hi @kramer99,

I fail to reproduce it. Do you have way to reproduce it on the react oidc demo?

guillaume-chervet avatar Apr 25 '24 10:04 guillaume-chervet

@guillaume-chervet

You can clone this: https://github.com/kramer99/oidc-test

...update _app.tsx with your IDP details and run. You should see the problem. I've tried it with two different IDPs (Okta and Google Cloud).

kramer99 avatar Apr 25 '24 21:04 kramer99

thank you @kramer99 , i can reproduce it with your demo :)

guillaume-chervet avatar Apr 26 '24 08:04 guillaume-chervet

hi @kramer99 ,

I have added preload_user_info: true ine the configuration and it works on my side :)

guillaume-chervet avatar Apr 26 '24 15:04 guillaume-chervet

I didn't notice a difference with or without the preload_user_info: true setting.

You cleared localStorage immediately before you tested successfully?

kramer99 avatar Apr 26 '24 20:04 kramer99

I have another piece of information, maybe it will help. right after the /token call returns with valid tokens, there is a call to the /userinfo endpoint which fails because no authorization header is supplied. Then a second /userinfo call is made right after which has the authorization header and thus succeds:

image

Here is that failed userinfo call as a CURL:

curl 'https://<removed>.okta.com/oauth2/v1/userinfo' \
  -H 'accept: application/json' \
  -H 'accept-language: en-US,en;q=0.9' \
  -H 'origin: http://localhost:3000' \
  -H 'referer: http://localhost:3000/' \
  -H 'sec-ch-ua: "Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "macOS"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: cross-site' \
  -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36'

(this is with preload_user_info: true, in the config)

kramer99 avatar Apr 26 '24 22:04 kramer99

@guillaume-chervet I'm experiencing similar issue where after login, the oidcUser object is initially null. Additionally, the subsequent call to the userinfo endpoint returns an unauthorized error. There is no additional call and have to refresh the page and then everything works fine.

Following is the configuration:

export const configurationIdentityServerWithoutServiceWorker = {
  client_id: 'interactive.public.short',
  redirect_uri: window.location.origin + '/callback',
  silent_redirect_uri: window.location.origin + '/silent-callback',
  scope: IDENTITY_SERVER_SCOPE,
  authority: 'http://localhost:8080',
  // authority_time_cache_wellknowurl_in_second: 60* 60,
  refresh_time_before_tokens_expiration_in_second: 40,
  storage: localStorage,
  //
  preload_user_info: true
};

Version: 7.22.4

attiqeurrehman avatar May 02 '24 15:05 attiqeurrehman

Hi @attiqeurrehman thank you for your feedback again. I will investigate it after my holiday near the 13 may.

Thank to all your details I think it will be easy to reproduce and debug.

guillaume-chervet avatar May 02 '24 19:05 guillaume-chervet

@guillaume-chervet any update on this?

attiqeurrehman avatar May 17 '24 10:05 attiqeurrehman

@attiqeurrehman @kramer99 I just push the fix.

Sorry, I had big holdidays time :)

guillaume-chervet avatar May 19 '24 13:05 guillaume-chervet

@guillaume-chervet no worries, hope you had a good time.

attiqeurrehman avatar May 19 '24 13:05 attiqeurrehman

@guillaume-chervet fix looks good on my side. I'll close this unless @attiqeurrehman objects.

kramer99 avatar May 20 '24 22:05 kramer99

Works like a charm. You can close this. Thanks a lot!

attiqeurrehman avatar May 21 '24 05:05 attiqeurrehman