Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
kramer99 opened this issue Apr 19, 2024 · 17 comments

Comments

@kramer99
Copy link

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: #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:
@guillaume-chervet
Copy link
Contributor

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

@guillaume-chervet
Copy link
Contributor

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

@kramer99
Copy link
Author

Confirmed, fix looks good on my side.

Thanks for the quick work @guillaume-chervet !

@kramer99
Copy link
Author

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 kramer99 reopened this Apr 24, 2024
@guillaume-chervet
Copy link
Contributor

Hi @kramer99,

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

@kramer99
Copy link
Author

@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).

@guillaume-chervet
Copy link
Contributor

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

@guillaume-chervet
Copy link
Contributor

hi @kramer99 ,

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

@kramer99
Copy link
Author

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

You cleared localStorage immediately before you tested successfully?

@kramer99
Copy link
Author

kramer99 commented Apr 26, 2024

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)

@attiqeurrehman
Copy link

attiqeurrehman commented May 2, 2024

@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

@guillaume-chervet
Copy link
Contributor

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.

@attiqeurrehman
Copy link

@guillaume-chervet any update on this?

@guillaume-chervet
Copy link
Contributor

@attiqeurrehman @kramer99 I just push the fix.

Sorry, I had big holdidays time :)

@attiqeurrehman
Copy link

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

@kramer99
Copy link
Author

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

@attiqeurrehman
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants