Open KhekhaevSalekh opened 3 months ago
The same problem with Yandex provider.
Terminal output:
GET /api/auth/callback/yandex?code=9756967&cid=47kjwvgdk0fxmxmfvuzyd46v2r 200 in 2886ms
error { error: 'invalid_grant', error_description: 'Code has expired' }
[auth][debug]: callback route error details {
"method": "GET",
"query": {
"code": "9756967",
"cid": "47kjwvgdk0fxmxmfvuzyd46v2r"
}
}
[auth][error] CallbackRouteError: Read more at https://errors.authjs.dev#callbackrouteerror
[auth][cause]: Error: TODO: Handle OAuth 2.0 response body error
at handleOAuth (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/actions/callback/oauth/callback.js:112:19)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
...
Also, sometimes there is an error with PKCE verification, but authorization occurs. Quite unstable provider.
[auth][error] InvalidCheck: PKCE code_verifier cookie was missing. Read more at https://errors.authjs.dev#invalidcheck
at Object.use (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/actions/callback/oauth/checks.js:57:19)
Environment
OS: Windows 10
Node: 20.11
Chrome 127.0.6533.120
next-auth: 5.0.0-beta.20
@auth/prisma-adapter: 2.4.2
next: 14.2.5
The same problem with Yandex provider.
Terminal output:
GET /api/auth/callback/yandex?code=9756967&cid=47kjwvgdk0fxmxmfvuzyd46v2r 200 in 2886ms error { error: 'invalid_grant', error_description: 'Code has expired' } [auth][debug]: callback route error details { "method": "GET", "query": { "code": "9756967", "cid": "47kjwvgdk0fxmxmfvuzyd46v2r" } } [auth][error] CallbackRouteError: Read more at https://errors.authjs.dev#callbackrouteerror [auth][cause]: Error: TODO: Handle OAuth 2.0 response body error at handleOAuth (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/actions/callback/oauth/callback.js:112:19) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) ...
Also, sometimes there is an error with PKCE verification, but authorization occurs. Quite unstable provider.
[auth][error] InvalidCheck: PKCE code_verifier cookie was missing. Read more at https://errors.authjs.dev#invalidcheck at Object.use (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/actions/callback/oauth/checks.js:57:19)
Environment
OS: Windows 10 Node: 20.11 Chrome 127.0.6533.120 next-auth: 5.0.0-beta.20 @auth/prisma-adapter: 2.4.2 next: 14.2.5
I spent about a week troubleshooting this error, and the only workable solution I’ve found so far involves opening a second tab. However, I'm not satisfied with this workaround.
It would be great if the next-auth developers could consider supporting an OAuth flow similar to the one used by the Yandex provider.
The issue originates from the line in the signIn function from next-auth/react/index.js
:
window.location.href = url
.
To investigate further, you can insert a process.exit() command right before this line and log the URL for different providers (I tested with Google, GitHub, and Yandex). Then, in your browser's console, manually click on the URI. The behavior for Yandex differs from the others. It likely needs something like
window.open(url)
.
Regarding your second issue related to PKCE, you shouldn’t encounter this error if you haven't modified the default settings for the Yandex provider. Yandex defaults to using checks='state', so perhaps this parameter was changed (by you). Although I haven't extensively used it, even with the checks=['state', 'pkce']
setting, I didn't experience the same error.
I will continue to add different approachs I found in the repository which is attached to this issue (reproduction url). I hope that next-auth developers will answer. Maybe I missed something and the error can be solved straightforward.
The first approach is not acceptable (second tab) because it looks strange relative to the Google or GitHub. I will write second approach. I would like to read your thoughts. Is it acceptable approach? For my local and prod this works fine for now.
1) In Yandex Oauth API console for REDIRECT_URI
we use something like this http://localhost:3000/api/customYandex
.
2) In next-auth options configuration file for Yandex provider we use:
Yandex({
clientId: process.env.YANDEX_CLIENT_ID!,
clientSecret: process.env.YANDEX_CLIENT_SECRET!,
authorization:{
params:{
redirect_uri:`${process.env.NEXTAUTH_URL}/api/customYandex`
},
},
allowDangerousEmailAccountLinking: true,
httpOptions: {
timeout: 10000,
},
}),
In openIDClient library there is by default written 10 sec. So maybe there is no need to write timeout: 10000
. But when I saw this code for first time this parameter was written already. So I decide to not change this.
3) If in this api we write
import {NextRequest, NextResponse } from "next/server";
import { cookies } from 'next/headers';
export async function GET(req : NextRequest) {
const url = new URL(req.url);
console.log('URL', url)
const code = url.searchParams.get('code');
const cid = url.searchParams.get('cid');
const state = url.searchParams.get('state');
const redirectUrl = ${process.env.NEXTAUTH_URL}/api/auth/callback/yandex?code=${code}&cid=${cid}&state=${state};
return NextResponse.redirect(redirectUrl);
}
we get two consols and the error `invalid_grant (code has expired).
3) So I change this code like this:
import {NextRequest, NextResponse } from "next/server";
import { cookies } from 'next/headers';
export async function GET(req : NextRequest) {
const url = new URL(req.url);
const code = url.searchParams.get('code');
const cid = url.searchParams.get('cid');
const state = url.searchParams.get('state');
const cookieStore = cookies();
const firstRequestReceived = cookieStore.get('yandex_oauth_first_received');
if (!firstRequestReceived) {
cookieStore.set('yandex_oauth_first_received', 'true', {
maxAge: 15,
path: '/api/customYandex',
});
return new Response(null, { status: 204 });
}
cookieStore.set('yandex_oauth_first_received', 'false', {
maxAge: -1,
path: '/api/customYandex',
});
const redirectUrl = ${process.env.NEXTAUTH_URL}/api/auth/callback/yandex?code=${code}&cid=${cid}&state=${state};
return NextResponse.redirect(redirectUrl);
}
What do you think about it? What problem can arise from this approach?
The approach above works. But 1 of 20 attempts failed. I noted that it is enough to set timer like this:
import { NextResponse } from "next/server";
export async function GET(req) {
const url = new URL(req.url);
const code = url.searchParams.get('code');
const cid = url.searchParams.get('cid');
await new Promise(resolve => setTimeout(resolve, 5000));
const redirectUrl = `http://localhost:3000/api/auth/callback/yandex?code=${code}&cid=${cid}`;
return NextResponse.redirect(redirectUrl);
}
The approach above works. But 1 of 20 attempts failed. I noted that it is enough to set timer like this:
import { NextResponse } from "next/server"; export async function GET(req) { const url = new URL(req.url); const code = url.searchParams.get('code'); const cid = url.searchParams.get('cid'); await new Promise(resolve => setTimeout(resolve, 5000)); const redirectUrl = `http://localhost:3000/api/auth/callback/yandex?code=${code}&cid=${cid}`; return NextResponse.redirect(redirectUrl); }
Thanks for this workaround, it's worked for me. But I noticed that the described problem only occurs when the app starts "cold" (e.g. right after I run next dev and there is no cache for components). Experimentally, I found out that a 3 seconds timeout is enough for me.
Provider type
Yandex
Environment
Reproduction URL
https://github.com/KhekhaevSalekh/next-auth-Yandex-issue
Describe the issue
When I use Yandex provider with next-auth all goes well. But when I connect hasura adapter i get error.
The error I get is (invalid_grant(code has expired)). When a new user presses "Sign in" by Yandex he does not go to home page. He appears at sign-in page. Also there is not next-auth.session-token coockie. In the same time all necessary information is written in the database. When he presses "Sign in" by Yandex second time he is on the home page.
With Google and GitHub authorization everything is good. I have checked this behavior on vercel and locally.
How to reproduce
Just start project from Reproduction URL and try to sighin with yandex.
Expected behavior
Normal sign in flow without this error.