Hi, my Auth0 app is on Next.js 14.1.4 App Router. I am looking to create a login / signup button that takes my users directly to a social login. In my case the Google / Gmail (google-oauth2) screen, while bypassing the Auth0 login prompt, eliminating an extra step for Gmail users. I already have both (passwordless) email & Google authentication working 100% without any issues. Universal Login is configured, with the Identifier First login flow for the passwordless experience.
My application is already established for a few years since the Next.js 13 days, following the steps from Auth0’s docs below. Can’t upgrade from 14.1.4 to 14.2.* due to unrelated issues here which breaks my & other users’ code: Incompatibility with next ^14.2.4 · Issue #1776 · auth0/nextjs-auth0 · GitHub. However this thread is on a different subject.
My app router route is defined under app/api/auth/[auth0]/route.js
as:
import { handleAuth } from '@auth0/nextjs-auth0';
export const GET = handleAuth();
As of now, the login feature is wrapped around a button as the below:
<a href="/api/auth/login"><button>Login</button></a>
Attempt #1 out of #6
My first attempt was appending this url with ?connection=google-oauth2
, which still takes me directly to the Auth0 login page instead of directly to Gmail.
Attempt #2 out of #6
This involves creating a new route:
api/auth/login-google
Within this route I have the below:
import { handleLogin } from '@auth0/nextjs-auth0';
export const GET = handleLogin({
authorizationParams: {
connection: 'google-oauth2'
}
});
Great, this works if I npm run dev
my app, and I’m able to sign-in with Google & return to my app logged in. However this results in build errors:
src/app/api/auth/login-google/route.js
Type error: Route "src/app/api/auth/login-google/route.js" has an invalid "GET" export:
Type "NextRequest | NextApiRequest" is not a valid type for the function's first argument.
Expected "Request | NextRequest", got "NextRequest | NextApiRequest".
Expected "Request | NextRequest", got "NextApiRequest".
Attempt #3 out of #6
I now modify my login-google/route.js with:
import { handleLogin } from '@auth0/nextjs-auth0';
export const dynamic = "force-dynamic";
export async function GET(request) {
return handleLogin(request, {
authorizationParams: {
connection: 'google-oauth2'
}
});
}
This now directs me back to the Auth0 login page, instead of directly to Gmail. Not what I want.
Attempt #4 out of #6
The suggestion is to manualy build the login with query parameters:
export const dynamic = "force-dynamic";
export async function GET() {
const auth0BaseUrl = process.env.AUTH0_BASE_URL;
const auth0Callback = process.env.AUTH0_CALLBACK
const auth0IssuerBaseUrl = process.env.AUTH0_ISSUER_BASE_URL;
const clientId = process.env.AUTH0_CLIENT_ID;
const redirectUri = `${auth0BaseUrl}${auth0Callback}`;
const loginUrl = `${auth0IssuerBaseUrl}/authorize?` +
new URLSearchParams({
client_id: clientId,
redirect_uri: redirectUri,
response_type: 'code',
connection: 'google-oauth2'
});
return new Response(null, {
status: 302,
headers: { Location: loginUrl.toString() },
});
}
I can build my project without errors, and clicking my button takes me directly to my Google login, however, after logging in I receive an HTTP ERROR 400 at my api/auth0/callback
route.
Attempt #5 out of #6
At this point I’m maybe doing to much and perhaps I should’ve asked here before more attempts, but now the suggestion is to redo my api/auth/[auth0]/route.js
to properly catch the callback
route instead of returning HTTP ERROR 400.
I change from:
import { handleAuth } from '@auth0/nextjs-auth0';
export const GET = handleAuth();
To:
import { handleAuth, handleLogin, handleLogout, handleCallback, handleProfile } from '@auth0/nextjs-auth0';
export const dynamic = 'force-dynamic';
export async function GET(request) {
const url = new URL(request.url);
const pathname = url.pathname;
try {
if (pathname.endsWith('/login')) {
return await handleLogin(request, {
authorizationParams: {
connection: 'google-oauth2',
scope: 'openid profile email',
},
});
} else if (pathname.endsWith('/logout')) {
return await handleLogout(request);
} else if (pathname.endsWith('/callback')) {
return await handleCallback(request);
} else if (pathname.endsWith('/me')) {
return await handleProfile(request);
} else {
return handleAuth()(request);
}
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: error.status || 400,
headers: { 'Content-Type': 'application/json' },
});
}
}
Ok, I can build without issues and login, but then the callback
route returns the error:
{"error":"Callback handler failed. CAUSE: Missing state cookie from login request (check login URL, callback URL and cookie config)."}
Attempt #6 out of #6
I then rewrite my app/api/auth/login-google/route.js
endpoint with:
import { randomBytes } from 'crypto';
export const dynamic = "force-dynamic";
export async function GET() {
const auth0BaseUrl = process.env.AUTH0_BASE_URL;
const auth0Callback = process.env.AUTH0_CALLBACK;
const auth0IssuerBaseUrl = process.env.AUTH0_ISSUER_BASE_URL;
const clientId = process.env.AUTH0_CLIENT_ID;
const redirectUri = `${auth0BaseUrl}${auth0Callback}`;
const desiredLength = 64;
const state = randomBytes(Math.ceil(desiredLength / 2)).toString('hex').slice(0, desiredLength);
const loginUrl = `${auth0IssuerBaseUrl}/authorize?` +
new URLSearchParams({
client_id: clientId,
redirect_uri: redirectUri,
response_type: 'code',
connection: 'google-oauth2',
state: state,
}).toString();
return new Response(null, {
status: 302,
headers: {
Location: loginUrl,
'Set-Cookie': `auth_state=${state}; HttpOnly; Secure; Path=/; SameSite=Lax`,
},
});
}
Returns the error:
{"error":"Callback handler failed. CAUSE: state mismatch, expected ********************************************************, got: ********************************************************"}
These are 2 different state codes btw. This is where I give up, perhaps there’s an Auth0 recommended way of generating the state within my login-google
route, or maybe there’s even simpler overall code.
Any help would be greatly appreciated, thanks!