I'm trying to implement authentication using NextAuth.js with the Credentials Provider. However, when I try to sign in, I get the following error:
[auth][error] CallbackRouteError: Read more at #callbackrouteerror
[auth][cause]: TypeError: fetch failed
Here is my app/lib/auth.js file:
import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
const URL = process.env.NEXT_PUBLIC_API_URL;
const Token = process.env.NEXT_PUBLIC_TOKEN;
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
Credentials({
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
authorize: async (credentials) => {
const { email, password } = credentials;
console.log(email, password);
console.log(`${URL}auth/jwt/create/`);
const body = JSON.stringify({ email, password });
const res = await fetch(`${URL}auth/jwt/create/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body,
});
if (!res.ok) {
throw new Error("Failed to sign in");
} else {
const data = await res.json();
return data;
}
},
}),
],
});
My login component:
import { signIn } from "@/app/lib/auth";
import { executeAction } from "../lib/executeAction";
function Page() {
return (
<main>
<form
action={async (formData) => {
"use server";
await executeAction({
actionFn: async () => {
await signIn("credentials", formData);
},
});
}}
>
<h1>Sign in</h1>
<input name="email" type="email" required />
<input name="password" type="password" required />
<button type="submit">Sign in</button>
</form>
</main>
);
}
export default Page;
My API route at app/api/auth/[...nextauth]/route.js:
import { handlers } from "@/app/lib/auth";
export const { GET, POST } = handlers;
What I've checked so far:
- NEXT_PUBLIC_API_URL and NEXT_PUBLIC_TOKEN are correctly set in my .env.local file.
- The backend URL and the endpoint (auth/jwt/create/) are correct.
- The backend is running and reachable via Postman/cURL.
Despite this, fetch fails inside authorize. What could be causing this, and how can I fix it?
I'm trying to implement authentication using NextAuth.js with the Credentials Provider. However, when I try to sign in, I get the following error:
[auth][error] CallbackRouteError: Read more at https://errors.authjs.dev#callbackrouteerror
[auth][cause]: TypeError: fetch failed
Here is my app/lib/auth.js file:
import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
const URL = process.env.NEXT_PUBLIC_API_URL;
const Token = process.env.NEXT_PUBLIC_TOKEN;
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
Credentials({
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
authorize: async (credentials) => {
const { email, password } = credentials;
console.log(email, password);
console.log(`${URL}auth/jwt/create/`);
const body = JSON.stringify({ email, password });
const res = await fetch(`${URL}auth/jwt/create/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body,
});
if (!res.ok) {
throw new Error("Failed to sign in");
} else {
const data = await res.json();
return data;
}
},
}),
],
});
My login component:
import { signIn } from "@/app/lib/auth";
import { executeAction } from "../lib/executeAction";
function Page() {
return (
<main>
<form
action={async (formData) => {
"use server";
await executeAction({
actionFn: async () => {
await signIn("credentials", formData);
},
});
}}
>
<h1>Sign in</h1>
<input name="email" type="email" required />
<input name="password" type="password" required />
<button type="submit">Sign in</button>
</form>
</main>
);
}
export default Page;
My API route at app/api/auth/[...nextauth]/route.js:
import { handlers } from "@/app/lib/auth";
export const { GET, POST } = handlers;
What I've checked so far:
- NEXT_PUBLIC_API_URL and NEXT_PUBLIC_TOKEN are correctly set in my .env.local file.
- The backend URL and the endpoint (auth/jwt/create/) are correct.
- The backend is running and reachable via Postman/cURL.
Despite this, fetch fails inside authorize. What could be causing this, and how can I fix it?
Share Improve this question asked Apr 1 at 1:05 LucasLucas 1,5002 gold badges21 silver badges43 bronze badges1 Answer
Reset to default 0it seems that your not usign the correct format for the credentials. Here is an example of the correct format based on the documentation. https://next-auth.js./providers/credentials
I think with the correct format it should be something like this
// Modified app/lib/auth.js
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
// Make sure we actually have these variables
const URL = process.env.NEXT_PUBLIC_API_URL || "";
const Token = process.env.NEXT_PUBLIC_TOKEN || "";
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
try {
if (!URL) {
console.error("API URL is not defined");
return null;
}
const { email, password } = credentials;
console.log(`Attempting to authenticate: ${email}`);
console.log(`Endpoint: ${URL}auth/jwt/create/`);
// Full URL with protocol
const fullUrl = URL.startsWith('http') ? `${URL}auth/jwt/create/` : `https://${URL}auth/jwt/create/`;
const response = await fetch(fullUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
// Add the token if needed for authentication
...(Token && { "Authorization": `Bearer ${Token}` })
},
body: JSON.stringify({ email, password }),
});
if (!response.ok) {
console.error(`Auth failed with status: ${response.status}`);
return null;
}
const data = await response.json();
// Return user object that NextAuth expects
return {
id: email,
email,
// Include tokens from your API if needed
accessToken: data.access,
refreshToken: data.refresh
};
} catch (error) {
console.error("Authorization error:", error);
return null;
}
},
}),
],
callbacks: {
async jwt({ token, user }) {
// Initial sign in
if (user) {
return {
...token,
accessToken: user.accessToken,
refreshToken: user.refreshToken
};
}
return token;
},
async session({ session, token }) {
session.user.accessToken = token.accessToken;
session.user.refreshToken = token.refreshToken;
return session;
}
},
pages: {
signIn: '/login',
}
});