How to change auth error Location header #12060
-
I run a website that runs under several domains. When a user fails to sign in, they are redirected to the AUTH_URL instead of the domain where they are trying to log in. I've found where this is set but it's all abstracted away heavily so I cannot pinpoint where I can change this behaviour, if at all. Triggered from Say I set my AUTH_URL to exampleA.com https://exampleB.com/api/auth/callback/email?callbackUrl=... returns a 302 with the Location header to https://exampleA.com/auth-error?error=Verification when the login fails When the login is successful, I can customize the redirect behaviour with the following: redirect: async ({ url, baseUrl }) => {
if (url.startsWith("/")) return `${baseUrl}${url}`;
if (allowedRedirectDomains.includes(new URL(url).hostname)) return url;
return baseUrl;
},
How do I also change this for the error reponse? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
I'd much rather prefer handling the error myself. Because I also want to carry some information over to the error handling, namely the email. |
Beta Was this translation helpful? Give feedback.
-
sigh I copied and adapted the source code to fit my needs and I now use this custom route handler to handle logins. Sometimes I do wonder if I should just write things myself. Nextauth makes me spend so much time digging on how to customize it for my needs that I might as well implement everything myself... [Update] on the positive side, next auth is open source. So I can reimplement things myself easily! import { redirect } from "next/navigation";
import { type NextRequest } from "next/server";
import { type Prisma } from "@prisma/client";
import { addDays } from "date-fns";
import { cookies } from "next/headers";
import { createHash } from "~/server/auth";
import { db } from "~/server/db";
export async function doIt(token: string, email: string) {
const invite = await consumeVerificationToken({
identifier: email,
token: await createHash(token),
});
const hasInvite = !!invite;
const expired = hasInvite && invite.expires.valueOf() < Date.now();
const invalidInvite =
!hasInvite ||
expired ||
// The user might have configured the link to not contain the identifier
// so we only compare if it exists
(email && invite.identifier !== email);
if (invalidInvite) return { hasInvite, expired };
const { identifier } = invite;
let user = (await getUserByEmail(identifier)) ?? {
id: crypto.randomUUID(),
email: identifier,
emailVerified: null,
};
if (user) {
user = await db.user.update({
where: {
id: user.id,
},
data: {
emailVerified: new Date(),
},
});
} else {
user = await db.user.create({
data: {
email: identifier,
emailVerified: new Date(),
},
});
}
const cookiestore = await cookies();
const sessionToken = crypto.randomUUID();
const expires = addDays(new Date(), 3);
await db.session.create({
data: {
userId: user.id,
sessionToken,
expires,
},
});
cookiestore.set({
name: "authjs.session-token",
value: sessionToken,
expires,
secure: true,
httpOnly: true,
});
}
const getUserByEmail = async (email: string) =>
db.user.findUnique({ where: { email } });
async function consumeVerificationToken(identifier_token: {
identifier: string;
token: string;
}) {
try {
const verificationToken = await db.verificationToken.delete({
where: { identifier_token },
});
// @ts-expect-errors // MongoDB needs an ID, but we don't
if (verificationToken.id) delete verificationToken.id;
return verificationToken;
} catch (error) {
// If token already used/deleted, just return null
// https://www.prisma.io/docs/reference/api-reference/error-reference#p2025
if ((error as Prisma.PrismaClientKnownRequestError).code === "P2025")
return null;
throw error;
}
}
export async function GET(request: NextRequest) {
const nextUrl = request.nextUrl;
const params = nextUrl.searchParams;
const token = params.get("token");
const email = params.get("email");
if (!token || !email) redirect("/signin");
const error = await doIt(token, email);
if (error) {
redirect(
`/signin?expired=${error.expired}&invited=${error.hasInvite}&email=${encodeURIComponent(email)}`,
);
}
return redirect("/");
} |
Beta Was this translation helpful? Give feedback.
sigh I copied and adapted the source code to fit my needs and I now use this custom route handler to handle logins.
Sometimes I do wonder if I should just write things myself. Nextauth makes me spend so much time digging on how to customize it for my needs that I might as well implement everything myself...
[Update] on the positive side, next auth is open source. So I can reimplement things myself easily!