oidc-client
oidc-client copied to clipboard
useOidcUser briefly returning null on page navigation refresh during authenticated session [NextJs]
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:
...and in the console:
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:
Hi @kramer99 thank you for your issue and feedback. I will make more test to debug and enhance it !
version v7.22.4-alpha.1425 should work better @kramer99 :)
Confirmed, fix looks good on my side.
Thanks for the quick work @guillaume-chervet !
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:
Hi @kramer99,
I fail to reproduce it. Do you have way to reproduce it on the react oidc demo?
@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).
thank you @kramer99 , i can reproduce it with your demo :)
hi @kramer99 ,
I have added preload_user_info: true ine the configuration and it works on my side :)
I didn't notice a difference with or without the preload_user_info: true
setting.
You cleared localStorage immediately before you tested successfully?
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:
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)
@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
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 any update on this?
@attiqeurrehman @kramer99 I just push the fix.
Sorry, I had big holdidays time :)
@guillaume-chervet no worries, hope you had a good time.
@guillaume-chervet fix looks good on my side. I'll close this unless @attiqeurrehman objects.
Works like a charm. You can close this. Thanks a lot!