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)
...
}
``
`