next-auth
next-auth copied to clipboard
Navigating back after successful Azure AD B2C sign on throws OAuthCallback error for 'state' and 'pkce' check types
Provider type
Azure Active Directory B2C
Environment
System:
OS: Windows 10 10.0.22000
CPU: (16) x64 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz
Memory: 6.21 GB / 15.77 GB
Binaries:
Node: 16.13.1 - C:\Program Files\nodejs\node.EXE
npm: 8.1.2 - C:\Program Files\nodejs\npm.CMD
Browsers:
Edge: Spartan (44.22000.120.0), Chromium (103.0.1264.77)
Internet Explorer: 11.0.22000.120
npmPackages:
next: 12.2.4 => 12.2.4
next-auth: ^4.10.3 => 4.10.3
react: 18.2.0 => 18.2.0
Reproduction URL
https://github.com/jcbtheo/azure-ad-b2c-issue
Describe the issue
Using the out of the box Azure AD B2C provider, using the default provider check value to 'state' or 'pkce' will allow the user to login successfully. However, once the login is resolved, immediately navigating back in the browser will cause an OAuthCallback error.
The root of the issue seems to be that after logging in, the Azure AD B2C authorize URL is still in the browser history, with the previously used state or pkce query params in the URL, and next auth is unable (or does not provide a way) to handle the resulting error.
When 'state' is selected, the error is
[next-auth][error][OAUTH_CALLBACK_ERROR]
https://next-auth.js.org/errors#oauth_callback_error checks.state argument is missing {
error: {
message: 'checks.state argument is missing',
stack: 'TypeError: checks.state argument is missing\n' +
' at Client.callback (C:\\Dev\\AzureB2cNextIssue\\azure-ad-b2c-issue\\node_modules\\openid-client\\lib\\client.js:385:13)\n' +
' at oAuthCallback (C:\\Dev\\AzureB2cNextIssue\\azure-ad-b2c-issue\\node_modules\\next-auth\\core\\lib\\oauth\\callback.js:114:29)\n' +
' at runMicrotasks (<anonymous>)\n' +
' at processTicksAndRejections (node:internal/process/task_queues:96:5)\n' +
' at async Object.callback (C:\\Dev\\AzureB2cNextIssue\\azure-ad-b2c-issue\\node_modules\\next-auth\\core\\routes\\callback.js:50:11)\n' +
' at async NextAuthHandler (C:\\Dev\\AzureB2cNextIssue\\azure-ad-b2c-issue\\node_modules\\next-auth\\core\\index.js:186:28)\n' +
' at async NextAuthNextHandler (C:\\Dev\\AzureB2cNextIssue\\azure-ad-b2c-issue\\node_modules\\next-auth\\next\\index.js:23:19)\n' +
' at async C:\\Dev\\AzureB2cNextIssue\\azure-ad-b2c-issue\\node_modules\\next-auth\\next\\index.js:59:32\n' +
' at async Object.apiResolver (C:\\Dev\\AzureB2cNextIssue\\azure-ad-b2c-issue\\node_modules\\next\\dist\\server\\api-utils\\node.js:184:9)\n' +
' at async DevServer.runApi (C:\\Dev\\AzureB2cNextIssue\\azure-ad-b2c-issue\\node_modules\\next\\dist\\server\\next-server.js:381:9)',
name: 'TypeError'
at async NextAuthHandler (C:\Dev\AzureB2cNextIssue\azure-ad-b2c-issue\node_modules\next-auth\core\index.js:186:28)
at async NextAuthNextHandler (C:\Dev\AzureB2cNextIssue\azure-ad-b2c-issue\node_modules\next-auth\next\index.js:23:19)
at async C:\Dev\AzureB2cNextIssue\azure-ad-b2c-issue\node_modules\next-auth\next\index.js:59:32
at async Object.apiResolver (C:\Dev\AzureB2cNextIssue\azure-ad-b2c-issue\node_modules\next\dist\server\api-utils\node.js:184:9)
at async DevServer.runApi (C:\Dev\AzureB2cNextIssue\azure-ad-b2c-issue\node_modules\next\dist\server\next-server.js:381:9) {
name: 'OAuthCallbackError',
code: undefined
}
And when 'pkce' is selected:
[next-auth][error][OAUTH_CALLBACK_ERROR]
https://next-auth.js.org/errors#oauth_callback_error invalid_request (AADB2C90183: The supplied code_verifier is invalid
Correlation ID: 90409c6f-bd0e-4dba-81bb-28b6c2df9278
Timestamp: 2022-08-05 18:22:30Z
) {
error: {
message: 'invalid_request (AADB2C90183: The supplied code_verifier is invalid\r\n' +
'Correlation ID: 90409c6f-bd0e-4dba-81bb-28b6c2df9278\r\n' +
'Timestamp: 2022-08-05 18:22:30Z\r\n' +
')',
stack: 'OPError: invalid_request (AADB2C90183: The supplied code_verifier is invalid\r\n' +
'Correlation ID: 90409c6f-bd0e-4dba-81bb-28b6c2df9278\r\n' +
'Timestamp: 2022-08-05 18:22:30Z\r\n' +
Correlation ID: 90409c6f-bd0e-4dba-81bb-28b6c2df9278
Timestamp: 2022-08-05 18:22:30Z
)
at processResponse (C:\Dev\AzureB2cNextIssue\azure-ad-b2c-issue\node_modules\openid-client\lib\helpers\process_response.js:38:13)
at Client.grant (C:\Dev\AzureB2cNextIssue\azure-ad-b2c-issue\node_modules\openid-client\lib\client.js:1325:22)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Client.callback (C:\Dev\AzureB2cNextIssue\azure-ad-b2c-issue\node_modules\openid-client\lib\client.js:474:24)
at async oAuthCallback (C:\Dev\AzureB2cNextIssue\azure-ad-b2c-issue\node_modules\next-auth\core\lib\oauth\callback.js:114:16)
at async Object.callback (C:\Dev\AzureB2cNextIssue\azure-ad-b2c-issue\node_modules\next-auth\core\routes\callback.js:50:11)
at async NextAuthHandler (C:\Dev\AzureB2cNextIssue\azure-ad-b2c-issue\node_modules\next-auth\core\index.js:186:28)
at async NextAuthNextHandler (C:\Dev\AzureB2cNextIssue\azure-ad-b2c-issue\node_modules\next-auth\next\index.js:23:19)
at async C:\Dev\AzureB2cNextIssue\azure-ad-b2c-issue\node_modules\next-auth\next\index.js:59:32
at async Object.apiResolver (C:\Dev\AzureB2cNextIssue\azure-ad-b2c-issue\node_modules\next\dist\server\api-utils\node.js:184:9) {
name: 'OAuthCallbackError',
code: undefined
}
How to reproduce
Set up next auth for Azure B2C AD with 'state' or 'pkce' used for the check value in the provider options and sign in. Immediately after signing in navigate backwards in the browser. You will be thrown to the api/auth/signin?error=OAuthCallback page.
Expected behavior
Expected behavior would be for the user to be navigated back to login page, or redirected to the redirect url specified in the signOn function (this is the case when checks is specified as 'none').
In either case, I would hope that next auth would be able to recognize that a user was still logged in, and handle (or provide a way to handle) the possible errors resulting from re-requesting the Azure AD B2C authorization URL gracefully.
For anyone else who may be dealing with this issue, my current work around is this:
Use the pages option to overwrite the built in Next Auth sign in page with a custom one, which allows for the OAuthError query param to be appended custom page's route. Inside of the custom page I have the following:
// hacky fix for next auth error thrown when navigating back after successfully logging in
useEffect(() => {
if (query.error == "OAuthCallback") {
window.location.replace("/login");
}
});
While it works, it does mean that the login page now appears twice in a user's history, but at least that's better than an error page. It could also cover up possible legitimate OAuthCallback errors.
I touched on this above, but what's really odd is that after a successful login, the B2C authorize URL remains in the browser history. However, once you navigate back to that URL and trigger the OAuthCallback error, that URL is removed and replaced by the Next Auth signIn error page (I've dug through the code a bit, but I'm still unclear if this is done by Azure or by Next Auth).
If there was a way to remove the B2C URL after the initial successful sign on then this problem could be avoided entirely.
I myself have done a bit of digging about this bug and it seems this issue is not isolated to just Azure AD B2C. Since the bug only occurs under very specific conditions, there's a lot of confusion about what the fix is.
- https://github.com/nextauthjs/next-auth/issues/3251
- https://github.com/nextauthjs/next-auth/issues/4190
- https://github.com/nextauthjs/next-auth/issues/4570
- https://github.com/nextauthjs/next-auth/issues/5156
I attempted to open an issue on this when I also encountered the same issue with the Discord Provider, but it was immediately closed and turned into a discussion.
I encounter the same error with TwitterProvider, but only occasionally. It's not happening for all users and it's not happening in localhost. Did anyone find a solution to the problem?
I wonder if it's an enconding problem with query parameter on callback.
@ThangHuuVu any chance you've had a chance to look into this?
I'm also facing this issue on cloudlfare cdn works fine in local or not through cloud flare as soon as it goes through cloud flare falls over. Unsure what is going on or how to work around it
I had the same issue with Google Provider in development mode a while ago.
After some digging, I found the solution in my case:
You have to make sure that the NEXTAUTH_URL env variable is set to the correct host, i.e. in development it has to be http://localhost:PORT and the login request has to be started from localhost:PORT/your/login/page. If one of them is e.g. http://127.0.0.1:PORT the OAuthCallback error will be thrown.
Closing this as I believe we have found the correct solution for our Azure AD B2C implementation. I am unsure if it carries over to other adapters, but I presume it would in most cases (this example is in Next.js 14, but previous versions of Next.js will be similar).
In the api/auth/[...nextauth] route:
const handler = (req: NextRequest, res: NextResponse) => {
if (
req.nextUrl.pathname === "/api/auth/callback/azure-ad-b2c" &&
!req.cookies.has("next-auth.pkce.code_verifier") &&
!req.cookies.has("__Secure-next-auth.pkce.code_verifier")
) {
console.warn(
"No PKCE cookie was set when user navigated to B2C login flow. This can happen if the user navigated back after a successful login."
);
return NextResponse.redirect(`${process.env.NEXTAUTH_URL}`);
}
// ...any other route handler code you may have
return NextAuth(
req as unknown as NextApiRequest,
res as unknown as NextApiResponse,
options
);
};