Reputation: 16
I'm trying to manage jwt authentication with NextAuth 5 and Next.js 15. For some reason user is not saved inside my session object.
this is my auth.ts
file:
import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import { authConfig } from "./auth.config";
import { z } from "zod";
import { apiUrl } from "./app/lib/constants";
async function refreshAccessToken(token: any) {
try {
const response = await fetch(`${apiUrl}/auth/refresh`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ refreshToken: token.refreshToken }),
});
const data = await response.json();
if (!response.ok) throw data;
return {
...token,
accessToken: data.access_token,
accessTokenExpires: Date.now() + 60 * 60 * 1000,
refreshToken: data.refresh_token ?? token.refreshToken,
};
} catch (error) {
console.error("Refresh token error:", error);
return { ...token, error: "RefreshAccessTokenError" };
}
}
export const { handlers, auth, signIn, signOut } = NextAuth({
...authConfig,
providers: [
Credentials({
async authorize(credentials) {
const parsedCredentials = z
.object({ email: z.string().email(), password: z.string().min(3) })
.safeParse(credentials);
if (!parsedCredentials.success) return null;
const { email, password } = parsedCredentials.data;
try {
const response = await fetch(`${apiUrl}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
if (!response.ok) return null;
const data = await response.json();
return {
accessToken: data.accessToken,
refreshToken: data.refreshToken,
accessTokenExpires: Date.now() + 60 * 60 * 1000,
};
} catch (error) {
console.error("Login error:", error);
return null;
}
},
}),
],
callbacks: {
jwt: async ({ token, user }) => {
console.log({ ...token, ...user });
if (user) {
return { ...token, ...user };
}
if (Date.now() < token.accessTokenExpires) {
return token;
}
return await refreshAccessToken(token);
},
session: async ({ session, token }) => {
console.log("[Session Callback] Token:", token);
console.log("[Session Callback] Session:", session);
session.user = {
...session.user,
accessToken: token.accessToken,
refreshToken: token.refreshToken,
accessTokenExpires: token.accessTokenExpires,
};
return session;
},
},
});
and auth.config.ts
import type { NextAuthConfig } from "next-auth";
export const authConfig = {
secret: process.env.NEXTAUTH_SECRET,
session: { strategy: "jwt" },
pages: {
signIn: "/login",
},
callbacks: {
authorized({ auth, request: { nextUrl } }) {
console.log({ auth });
const isLoggedIn = !!auth?.user;
const isOnDashboard = nextUrl.pathname.startsWith("/dashboard");
if (isOnDashboard) {
if (isLoggedIn) return true;
return false;
} else if (isLoggedIn) {
return Response.redirect(new URL("/dashboard", nextUrl));
}
return true;
},
},
providers: \[\],
} satisfies NextAuthConfig;
I manage to log in but token is not saved in session. This is the console.log inside auth.config.ts for auth object: { auth: { user: {}, expires: '2025-04-02T21:31:41.402Z' } } Shouldn't user be stored in session callback? What did I miss? Why isn't my session callback triggered and informations are not saved in auth.user object?
Upvotes: 0
Views: 34
Reputation: 11319
I believe you need to extract access_token
, refresh_token
and expires_at
from account
.
Example code from my application, please note that I'm refreshing the access token when less than 10 seconds to expiration.
Please note that there's no need for adding refresh token to the session.
NextAuth 5 is still in beta, and there are still many issues that, according to some posts, makes it useless, see all issues.
callbacks: {
async jwt({token, user, account}) {
// Initial sign in
if (account && user) {
return {
accessToken: account.access_token,
accessTokenExpires: Number(account.expires_at) * 1000,
refreshToken: account.refresh_token,
user,
}
}
const expiresInSeconds = Math.floor((Number(token.accessTokenExpires) - Date.now()) / 1000)
console.debug("Access token expires in:", expiresInSeconds)
// Return previous token if the access token has not expired yet
if (token.error || expiresInSeconds > 10) {
return token
}
// Access token has expired, try to update it
return refreshAccessToken(token)
},
async session({session, token}) {
if (token) {
return {
...session,
user: token.user,
accessToken: token.accessToken,
error: token.error
} as Session
}
return session
},
// remaining config left out
Upvotes: 0
Reputation: 1
Make sure your jwt callback properly saves the user and token data when someone logs in, and then pass that data to the session callback so it can be used in the session. Double-check that your session strategy is set to "jwt" and that your NEXTAUTH_SECRET is correctly set in your environment file. This should fix the issue!
Upvotes: 0