I'm trying to solve an issue related to Next.js (port 3000) and Next-Auth. I currently have an external local API (port 3001) built using Nest.js, and I'm consuming the auth/login endpoint from there.
Important: I'm using the app folder.
I followed this post to configure next-auth in my project.
File app/api/auth/[...nextauth]/route.ts:
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
const handler = NextAuth({
providers: [
CredentialsProvider({
id: "credentials",
name: "my-project",
credentials: {
email: {
label: "email",
type: "email",
placeholder: "[email protected]",
},
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
const payload = {
email: credentials?.email,
password: credentials?.password,
};
try {
// also tried using `http://localhost:3001`
const res = await fetch("http://127.0.0.1:3001/auth/login", {
method: "POST",
body: JSON.stringify(payload),
headers: {
"Content-Type": "application/json",
},
});
const user = await res.json();
if (!res.ok) {
throw new Error(user.message);
}
if (res.ok && user) {
return user;
}
return null;
} catch (error: any) {
console.log(error);
}
},
}),
],
secret: process.env.NEXTAUTH_SECRET,
pages: {
signIn: "/login",
},
callbacks: {
async jwt({ token, user, account }: any) {
if (account && user) {
return {
...token,
accessToken: user.token,
refreshToken: user.refreshToken,
};
}
return token;
},
async session({ session, token }: any) {
session.user.accessToken = token.accessToken;
session.user.refreshToken = token.refreshToken;
session.user.accessTokenExpires = token.accessTokenExpires;
return session;
},
},
debug: process.env.NODE_ENV === "development",
});
export { handler as GET, handler as POST };
File: app/login/page.tsx:
"use client";
import {
...
} from "@chakra-ui/react";
import { useRouter } from "next/navigation";
import { ChangeEvent, useState } from "react";
import { signIn, getCsrfToken } from "next-auth/react";
import { GetServerSidePropsContext } from "next";
export default function Login({ csrfToken }: { csrfToken: string}) {
const router = useRouter();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleEmailChange = (e: ChangeEvent<HTMLInputElement>) =>
setEmail(e.target.value);
const handlePasswordChange = (e: ChangeEvent<HTMLInputElement>) =>
setPassword(e.target.value);
const handleSignIn = async () => {
const res = await signIn("credentials", {
redirect: false,
email,
password,
callbackUrl: `${window.location.origin}`,
});
console.log(res);
};
return (
<Container>
<FormControl>
...
</FormControl>
</Container>
);
}
export async function generateStaticParams(context: GetServerSidePropsContext) {
return {
props: {
csrfToken: await getCsrfToken(context),
},
};
}
File: app/providers.tsx:
"use client";
import { CacheProvider } from "@chakra-ui/next-js";
import { ChakraProvider } from "@chakra-ui/react";
import { extendTheme } from "@chakra-ui/react";
import { SessionProvider } from "next-auth/react";
export const theme = extendTheme({ colors, breakpoints });
export function Providers({ children }: { children: React.ReactNode }) {
return (
<SessionProvider>
<CacheProvider>
<ChakraProvider theme={theme}>{children}</ChakraProvider>
</CacheProvider>
</SessionProvider>
);
}
File: app/layout.tsx:
"use client";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<Head />
<body className={inter.className}>
<Providers>
<Container maxW={"90%"}>
<Grid minHeight={"100vh"} templateRows={"100px auto 50px"}>
<Header />
{children}
<Footer />
</Grid>
</Container>
</Providers>
</body>
</html>
);
}
What I'm trying to solve: The issue I'm facing is related to the fetch() call I'm making in the file app/api/auth/[...nextauth]/route.ts. Basically, at this point, I receive the following error:
TypeError: fetch failed at Object.fetch (node:internal/deps/undici/undici:11457:11) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) { cause: Error: connect ECONNREFUSED 127.0.0.1:3001 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1494:16) at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) { errno: -111, code: 'ECONNREFUSED', syscall: 'connect', address: '127.0.0.1', port: 3001 } }
I have tried making the same fetch call directly from app/login/page.tsx in the handleSignIn() method, and I obtained the token without any problems. This means that my API has nothing to do with this issue or any problems with the ports. But for some reason, when it goes to the Next-Auth definition, I get the error.
Sharing my docker compose file, just in case:
version: "3.8"
services:
pg-db:
image: postgres:15.1
restart: always
container_name: postgres
ports:
- "5432:5432"
nest-api:
...
depends_on:
- pg-db
env_file: ./.env
ports:
- "3001:3001"
stdin_open: true
tty: true
networks:
- default
nextjs-web:
...
ports:
- "3000:3000"
env_file: ./.env
volumes:
pg-db: