最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Next.js 15 NextAuth jwt - Stack Overflow

programmeradmin0浏览0评论

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?

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?

Share asked Mar 3 at 21:55 Luka BugarinLuka Bugarin 161 bronze badge 1
  • Issue resolved by moving jwt and session callbacks inside auth.config.ts file above authorized function in callbacks object. – Luka Bugarin Commented Mar 4 at 12:16
Add a comment  | 

2 Answers 2

Reset to default 0

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!

Issue resolved by moving jwt and session callbacks inside auth.config.ts file above authorized function in callbacks object. Can someone maybe explain why providers were working but callbacks didn't inside auth.ts? Is it maybe that session callback needs to be provided to middleware? This is my middleware.ts:

import NextAuth from 'next-auth';
import { authConfig } from './auth.config';

export default NextAuth(authConfig).auth;

export const config = {
// https://nextjs./docs/app/building-your- 
application/routing/middleware#matcher
matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
};
发布评论

评论列表(0)

  1. 暂无评论