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

how can i access my session in an api-route in NextAuth Auth.js Next.js 15? - Stack Overflow

programmeradmin2浏览0评论

I use NextJS 15 and Auth.js / NextAuth with the App Router.

I would like to protect my API route so that you can only gain access with the appropriate role. I would like to find this out from the session. However, it is not possible for me to access the session, and I simply don't know why.

Can anyone help me?

I make the API call in src/app/dashboard/accounts/page.tsx. In general, I can only enter this site if the session and role are valid.

src/app/dashboard/accounts/page.tsx

import { auth } from "@/lib/auth";
import { redirect } from "next/navigation";
import { checkPermissions } from "@/lib/permissions";

const Accounts = async () => {
  const session = await auth();
  if (!session || !checkPermissions(session.user, "view", "accounts")) {
    redirect("/dashboard");
  }

  const response = await fetch(
    `${process.env.NEXT_PUBLIC_API_URL}/api/accounts`,
    {
      method: "GET",
      credentials: "include",
    }
  );

Here it works that the session is checked and that the user role is checked.

What I don't understand, however, is why I can't access the session here?

src/app/api/accounts/route.ts

import { NextResponse } from "next/server";
import { auth } from "@/lib/auth";

export const GET = auth(function GET(req) {
  console.log(req)
  if (!req.auth) {
    return NextResponse.json({ message: "Not authenticated" }, { status: 401 });
  }

  console.log(req.auth);
  return NextResponse.json(req.auth);
});

I get this output here:

 ✓ Compiled /api/accounts in 615ms
Request {
  method: 'GET',
  url: 'https://localhost:3000/api/accounts',
  headers: Headers {
    accept: '*/*',
    'accept-encoding': 'br, gzip, deflate',
    'accept-language': '*',
    connection: 'keep-alive',
    host: 'localhost:3000',
    'sec-fetch-mode': 'cors',
    'user-agent': 'node',
    'x-forwarded-for': '::1',
    'x-forwarded-host': 'localhost:3000',
    'x-forwarded-port': '3000',
    'x-forwarded-proto': 'https'
  },
  destination: '',
  referrer: 'about:client',
  referrerPolicy: '',
  mode: 'cors',
  credentials: 'same-origin',
  cache: 'default',
  redirect: 'follow',
  integrity: '',
  keepalive: false,
  isReloadNavigation: false,
  isHistoryNavigation: false,
  signal: AbortSignal { aborted: false }
}

I do not use middleware.ts.
I have tried to use the Auth.js docs as a guide. Link to Auth.js session management docs.

I really don't know what to do, can anyone help me?
Thanks in advance

For context, here are a few important files:

/src/lib/auth.ts

import { v4 as uuid } from "uuid";
import bcrypt from "bcrypt";
import { encode as defaultEncode } from "next-auth/jwt";
import db from "@/lib/db/db";
import { PrismaAdapter } from "@auth/prisma-adapter";
import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import GitHub from "next-auth/providers/github";
import { schema } from "@/lib/schema";
import { User as PrismaUser, Role } from "@prisma/client";

const adapter = PrismaAdapter(db);

export const { handlers, signIn, signOut, auth } = NextAuth({
  adapter,
  providers: [
    GitHub,
    Credentials({
      credentials: {
        email: {},
        password: {},
      },
      authorize: async (credentials) => {
        const validatedCredentials = schema.parse(credentials);

        const user = await db.user.findFirst({
          where: {
            email: validatedCredentials.email,
          },
        });

        if (!user) {
          throw new Error("Invalid credentials.");
        }

        const passwordMatch =
          user.password &&
          (await bcryptpare(validatedCredentials.password, user.password));

        if (!passwordMatch) {
          throw new Error("Invalid credentials.");
        }
        return user;
      },
    }),
  ],
  callbacks: {
    async jwt({ token, account, user }) {
      if (account?.provider === "credentials") {
        token.credentials = true;
        token.accessToken = uuid();
      }

      if (user) {
        const prismaUser = user as PrismaUser & { role: string };

        token.id = String(prismaUser.id);
        token.name = prismaUser.name;
        token.email = prismaUser.email;
        token.picture = prismaUser.image;
        // token.role = prismaUser.role;
        // token.sub = prismaUser.id;
      }
      return token;
    },
    async session({ token, session }) {
      if (token) {
        session.user.id = token.sub!;
        session.accessToken = token.accessToken as string;
        session.user.name = token.name;
        session.user.image = token.picture;
        // session.user.role = token.role;
      }
      return session;
    },
    authorized: async ({ auth }) => {
      return !!auth;
    },
  },
  jwt: {
    encode: async function (params) {
      if (params.token?.credentials) {
        const sessionToken = uuid();

        if (!params.token.sub) {
          throw new Error("No user ID found in token");
        }

        const createdSession = await adapter?.createSession?.({
          sessionToken: sessionToken,
          userId: params.token.sub,
          expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
        });

        if (!createdSession) {
          throw new Error("Failed to create session");
        }

        return sessionToken;
      }
      return defaultEncode(params);
    },
  },
});

src/lib/permissions.ts

const PERMISSIONS: Record<string, string[]> = {
  ADMIN: ["view:accounts", "edit:accounts"],
};

export const checkPermissions = (
  user: { role?: string } | null,
  action: string,
  resource: string
): boolean => {
  if (!user || !user.role) return false;
  const permissions = PERMISSIONS[user.role];
  if (!permissions) return false;
  return permissions.includes(`${action}:${resource}`);
};

src/types/next-auth.d.ts

import { Role } from "@prisma/client";
import type { User } from "next-auth";
import "next-auth/jwt";

type UserId = string;

declare module "next-auth/jwt" {
  interface JWT {
    id: UserId;
    role: Role;
    accessToken?: string;
  }
}

declare module "next-auth" {
  interface Session {
    user: User & {
      id: UserId;
      role: Role;
      name?: string | null;
      email?: string | null;
      image?: string | null;
    };
    accessToken?: string;
  }
}

I use NextJS 15 and Auth.js / NextAuth with the App Router.

I would like to protect my API route so that you can only gain access with the appropriate role. I would like to find this out from the session. However, it is not possible for me to access the session, and I simply don't know why.

Can anyone help me?

I make the API call in src/app/dashboard/accounts/page.tsx. In general, I can only enter this site if the session and role are valid.

src/app/dashboard/accounts/page.tsx

import { auth } from "@/lib/auth";
import { redirect } from "next/navigation";
import { checkPermissions } from "@/lib/permissions";

const Accounts = async () => {
  const session = await auth();
  if (!session || !checkPermissions(session.user, "view", "accounts")) {
    redirect("/dashboard");
  }

  const response = await fetch(
    `${process.env.NEXT_PUBLIC_API_URL}/api/accounts`,
    {
      method: "GET",
      credentials: "include",
    }
  );

Here it works that the session is checked and that the user role is checked.

What I don't understand, however, is why I can't access the session here?

src/app/api/accounts/route.ts

import { NextResponse } from "next/server";
import { auth } from "@/lib/auth";

export const GET = auth(function GET(req) {
  console.log(req)
  if (!req.auth) {
    return NextResponse.json({ message: "Not authenticated" }, { status: 401 });
  }

  console.log(req.auth);
  return NextResponse.json(req.auth);
});

I get this output here:

 ✓ Compiled /api/accounts in 615ms
Request {
  method: 'GET',
  url: 'https://localhost:3000/api/accounts',
  headers: Headers {
    accept: '*/*',
    'accept-encoding': 'br, gzip, deflate',
    'accept-language': '*',
    connection: 'keep-alive',
    host: 'localhost:3000',
    'sec-fetch-mode': 'cors',
    'user-agent': 'node',
    'x-forwarded-for': '::1',
    'x-forwarded-host': 'localhost:3000',
    'x-forwarded-port': '3000',
    'x-forwarded-proto': 'https'
  },
  destination: '',
  referrer: 'about:client',
  referrerPolicy: '',
  mode: 'cors',
  credentials: 'same-origin',
  cache: 'default',
  redirect: 'follow',
  integrity: '',
  keepalive: false,
  isReloadNavigation: false,
  isHistoryNavigation: false,
  signal: AbortSignal { aborted: false }
}

I do not use middleware.ts.
I have tried to use the Auth.js docs as a guide. Link to Auth.js session management docs.

I really don't know what to do, can anyone help me?
Thanks in advance

For context, here are a few important files:

/src/lib/auth.ts

import { v4 as uuid } from "uuid";
import bcrypt from "bcrypt";
import { encode as defaultEncode } from "next-auth/jwt";
import db from "@/lib/db/db";
import { PrismaAdapter } from "@auth/prisma-adapter";
import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import GitHub from "next-auth/providers/github";
import { schema } from "@/lib/schema";
import { User as PrismaUser, Role } from "@prisma/client";

const adapter = PrismaAdapter(db);

export const { handlers, signIn, signOut, auth } = NextAuth({
  adapter,
  providers: [
    GitHub,
    Credentials({
      credentials: {
        email: {},
        password: {},
      },
      authorize: async (credentials) => {
        const validatedCredentials = schema.parse(credentials);

        const user = await db.user.findFirst({
          where: {
            email: validatedCredentials.email,
          },
        });

        if (!user) {
          throw new Error("Invalid credentials.");
        }

        const passwordMatch =
          user.password &&
          (await bcryptpare(validatedCredentials.password, user.password));

        if (!passwordMatch) {
          throw new Error("Invalid credentials.");
        }
        return user;
      },
    }),
  ],
  callbacks: {
    async jwt({ token, account, user }) {
      if (account?.provider === "credentials") {
        token.credentials = true;
        token.accessToken = uuid();
      }

      if (user) {
        const prismaUser = user as PrismaUser & { role: string };

        token.id = String(prismaUser.id);
        token.name = prismaUser.name;
        token.email = prismaUser.email;
        token.picture = prismaUser.image;
        // token.role = prismaUser.role;
        // token.sub = prismaUser.id;
      }
      return token;
    },
    async session({ token, session }) {
      if (token) {
        session.user.id = token.sub!;
        session.accessToken = token.accessToken as string;
        session.user.name = token.name;
        session.user.image = token.picture;
        // session.user.role = token.role;
      }
      return session;
    },
    authorized: async ({ auth }) => {
      return !!auth;
    },
  },
  jwt: {
    encode: async function (params) {
      if (params.token?.credentials) {
        const sessionToken = uuid();

        if (!params.token.sub) {
          throw new Error("No user ID found in token");
        }

        const createdSession = await adapter?.createSession?.({
          sessionToken: sessionToken,
          userId: params.token.sub,
          expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
        });

        if (!createdSession) {
          throw new Error("Failed to create session");
        }

        return sessionToken;
      }
      return defaultEncode(params);
    },
  },
});

src/lib/permissions.ts

const PERMISSIONS: Record<string, string[]> = {
  ADMIN: ["view:accounts", "edit:accounts"],
};

export const checkPermissions = (
  user: { role?: string } | null,
  action: string,
  resource: string
): boolean => {
  if (!user || !user.role) return false;
  const permissions = PERMISSIONS[user.role];
  if (!permissions) return false;
  return permissions.includes(`${action}:${resource}`);
};

src/types/next-auth.d.ts

import { Role } from "@prisma/client";
import type { User } from "next-auth";
import "next-auth/jwt";

type UserId = string;

declare module "next-auth/jwt" {
  interface JWT {
    id: UserId;
    role: Role;
    accessToken?: string;
  }
}

declare module "next-auth" {
  interface Session {
    user: User & {
      id: UserId;
      role: Role;
      name?: string | null;
      email?: string | null;
      image?: string | null;
    };
    accessToken?: string;
  }
}
Share Improve this question edited Mar 21 at 2:19 Hamed Jimoh 1,3898 silver badges20 bronze badges asked Mar 17 at 12:35 HannesHannes 11 bronze badge
Add a comment  | 

1 Answer 1

Reset to default 0

In Next.js v15, you should use getToken method to access your session in an api route

Here's an example

import { getToken, JWT } from "next-auth/jwt"

export async function GET(request: Request) {
    const token: JWT | null = await getToken({ req: request, secret: process.env.AUTH_SECRET })

    if (!token)
        return Response.json({
            success: false,
            message: "Unauthorized",
            data: null,
        },  { status: 401 })

    // then you can get your access token like this: token?.accessToken
}
发布评论

评论列表(0)

  1. 暂无评论