1

I am trying to make the authentication flow for NextJS 15 with app routing. I managed to set up the login logic, saving the encrypted authentication session in a cookie and the logout flow. Now the problem happens when I try to fetch the session in order to use it in my app (for example to display the user's name in the header). I checked the logs and the session endpoint keeps getting called to infinity: logs output Moreover, I want to get the name of the user from the session and it is null:

import { useSession } from "next-auth/react" 
export default function Header() {
  const { data: session } = useSession(); 

return (<div>...
          <span className="text-sm text-gray-600">{session?.user? .email || "None"</span>
...
</div>)

This is my setup:

src/app/api/auth/[...nextauth]/source.ts

import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { login } from "@/app/actions/auth";
import { LoginRequest, LoginResponse } from "@/lib/types"; // Optional: types
import { JWT } from "next-auth/jwt";
import { NextAuthOptions,SessionStrategy } from "next-auth";
import { NextResponse } from "next/server";

export const authOptions: NextAuthOptions = {
  providers: [
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: { label: "Email", type: "text", placeholder: "[email protected]" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) {
          throw new Error("Missing credentials");
        }

        const loginRequest: LoginRequest = {
          email: credentials.email,
          password: credentials.password,
        };

        try {
          const loginResponse: LoginResponse = await login(loginRequest);
          const user = loginResponse.user;

          if (!user || !user.id) {
            return null;
          }

          return {
            id: user.id,
            email: user.email,
            username: user.username,
            token: loginResponse.token,
          };
        } catch (err) {
          console.error("Auth error:", err);
          return null;
        }
      },
    }),
  ],
  session: {
    strategy: "jwt",
    maxAge: 60 * 60 * 24 * 30, // 30 days
  },
  callbacks: {
    async jwt({ token, user }: { token: JWT; user?: any }) {
        if (user) {
        token.id = user.id;
        token.email = user.email;
        token.username = user.username;
        token.token = user.token;
      }
      return token;
    },
    async session({ session, token }: { session: any; token: JWT }) {
        session.user.id = token.id;
      session.user.email = token.email;
      session.user.username = token.username;
      session.user.token = token.token;
      return session;
    },
  },
  secret: process.env.NEXTAUTH_SECRET, 
};


export async function GET(req: Request) {
  const authResponse = await NextAuth(authOptions);
  return NextResponse.json(authResponse);
}

export async function POST(req: Request) {
  const authResponse = await NextAuth(authOptions);
  return NextResponse.json(authResponse);
}

src/app/api/auth/session/source.ts

import { getSession } from "next-auth/react";
import { NextResponse } from "next/server";

export async function GET(req: any) {
  // Fetch session data
  try{
    const session = await getSession({ req });

  // If session exists, return it
    if (session) {
    console.log("Session found:", session);

      return NextResponse.json(session, { status: 200 });
    }
    console.log("No session found.");

    // If no session exists, return null with status 200
    return NextResponse.json(null, { status: 200 });
  }catch(error){
    console.log(error)
    return NextResponse.json(null, { status: 500 });

  }

 
}

// app/api/set-cookie/route.ts
import { NextResponse } from 'next/server';
import { encrypt } from '@/lib/session';

export async function POST(req: Request) {
  const { token } = await req.json();

  const base64Payload = token.split('.')[1]; // header.payload.signature
  const decoded = Buffer.from(base64Payload, 'base64').toString();
  const payload = JSON.parse(decoded);
console.log(`decoded ${decoded}\n payload`, payload)
  const sessionPayload = {
    userId: payload.sub,
    email: payload.email,
    name: payload.name,
  };

  const encryptedSession = await encrypt(sessionPayload);

  const response = NextResponse.json({ message: 'Session set' });

  response.cookies.set('session', encryptedSession, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'lax',
    path: '/',
    maxAge: 60 * 60 * 24, // 1 day
  });

  return response;
}
//src/app/[locale]/actions/auth.ts
export async function login(values: { email: string; password: string }) {
    //todo change it later
    const res = await fetch('http://127.0.0.1:8000/login/', {
      method: 'POST',
      body: JSON.stringify(values),
      headers: {
        'Content-Type': 'application/json',
      },
      credentials: 'include',
    });
  
    if (!res.ok) {
      throw new Error('Login failed');
    }
  
    return res.json(); 
  }
  

UPDATE: I removed the custom session which fixed the recursion problem, but the session is still null in the header, although i can see from the logs in the middleware that the token is properly stored. What is the proper way of getting the authentication session?

//middleware.ts
export async function middleware(req: NextRequest) {

...
 const cookie = (await cookies()).get('session')?.value;
 const session = await decrypt(cookie);
 console.log('session', session)
...
}
``
`
1
  • Hello, if you add the typscript tag, this will help SO activate syntax coloring on your code blocks. Commented Apr 8 at 19:39

2 Answers 2

0

You shouldn't define a custom session route when using next-auth, its already handled by NextAuth. Your custom route leads it to recursion.

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

2 Comments

that fixed the recursion problem, thank you! However, I still have issues with getting the session info in the header. I updated the post.
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
0

middleware.ts runs on Edge Runtime, not in your server environment. That’s the reason why session does not work there. It’s better to get the token using next-auth/jwt, e.g.:

import { getToken } from 'next-auth/jwt'
import { NextResponse } from 'next/server'

export async function middleware(req) {
  const secret = process.env.NEXTAUTH_SECRET
  const token = await getToken({ req, secret })

  // if !token then redirect to login URL

  return NextResponse.next()
}

Alternatively, try the option runtime: 'nodejs' to force your middleware function to run in Node.js runtime:

//middleware.ts
export const config = {
  runtime: 'nodejs',
}

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.