0

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:

1 Answer 1

2

Not really sure why but changing the URL from http://localhost:3001 or http://127.0.0.1:3001 to:

http://nest-api:3001

Resolved the issue for me. In my Docker Compose configuration, I have the API service named nest-api. By using the service name as the hostname in the URL, Docker's bridge network is able to resolve the name to the corresponding container IP address automatically. This allows successful communication between the Next.js application and the Nest.js API service within the Docker Compose network.

If anyone wants to explain better, feel free to do so.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.