You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next-auth技术问询:如何在Next.js API路由中使用Keycloak的Bearer令牌

问题:Next.js App Router + NextAuth + Keycloak:如何在API路由中获取认证令牌调用外部API?

我们基于Next.js App Router开发应用,用NextAuth集成Keycloak做OAuth认证,现在需要在Next API路由里调用外部REST API,但遇到了问题:

  • 在API路由中调用getServerSession,返回的session里user对象是空的(所有字段都是undefined)
  • 但在客户端组件里调用getServerSession却能拿到完整的JWT用户对象

相关代码与输出

API路由代码 (/api/data/list/route.ts)

export async function GET(request: NextRequest) {
    const session = await getServerSession();
    console.log('in route', session?.user);

    const apiServiceUrl = `${process.env.EXTERNAL_API_URL}/getData`;
    try {
        const apiResponse = await fetch(apiServiceUrl, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${session?.user}`
            }
        });
        // ...

终端输出

○ Compiling /api/data/list ...
 ✓ Compiled /api/data/list in 1482ms (719 modules)
in route { name: undefined, email: undefined, image: undefined }
 GET /api/data/list 200 in 1657ms

解决方案

1. 修正NextAuth配置,同步Keycloak令牌到Session

在NextAuth的配置文件(比如app/api/auth/[...nextauth]/route.ts)中,需要通过回调函数把Keycloak返回的令牌同步到session对象中,同时确保session策略为JWT:

import NextAuth from "next-auth";
import KeycloakProvider from "next-auth/providers/keycloak";

export const authOptions = {
  providers: [
    KeycloakProvider({
      clientId: process.env.KEYCLOAK_CLIENT_ID!,
      clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!,
      issuer: process.env.KEYCLOAK_ISSUER!,
    }),
  ],
  // 开启JWT会话策略(App Router推荐)
  session: { strategy: "jwt" },
  callbacks: {
    // 把Keycloak令牌注入session
    async session({ session, token }) {
      session.accessToken = token.accessToken;
      session.idToken = token.idToken;
      // 补全标准化用户信息
      session.user = {
        ...session.user,
        id: token.sub,
        email: token.email,
        name: token.name,
      };
      return session;
    },
    // 保存Keycloak令牌到JWT中
    async jwt({ token, account }) {
      if (account) {
        token.accessToken = account.access_token;
        token.idToken = account.id_token;
        token.expiresAt = account.expires_at;
      }
      return token;
    },
  },
};

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

2. 在API路由中正确获取令牌

修改API路由代码,必须显式传入authOptions调用getServerSession,并使用session中的accessToken作为认证令牌:

import { getServerSession } from "next-auth/next";
import { authOptions } from "../auth/[...nextauth]/route";
import { NextRequest } from "next/server";

export async function GET(request: NextRequest) {
    // 必须传入authOptions才能正确解析会话
    const session = await getServerSession(authOptions);
    console.log('in route', session?.accessToken, session?.user);

    // 校验令牌是否存在
    if (!session?.accessToken) {
        return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401 });
    }

    const apiServiceUrl = `${process.env.EXTERNAL_API_URL}/getData`;
    try {
        const apiResponse = await fetch(apiServiceUrl, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json',
                // 使用Keycloak颁发的accessToken
                'Authorization': `Bearer ${session.accessToken}`
            }
        });
        const data = await apiResponse.json();
        return new Response(JSON.stringify(data), { status: 200 });
    } catch (error) {
        return new Response(JSON.stringify({ error: "Failed to fetch data" }), { status: 500 });
    }
}

关键注意点

  • API路由必须传authOptions:App Router环境下,getServerSession需要显式传入配置才能正确读取会话,否则会出现user字段为空的情况。
  • 区分user对象与令牌:session.user是NextAuth标准化的用户信息,而Keycloak的认证令牌存储在accessToken字段中,不能直接用session.user作为Bearer令牌。
  • JWT策略必开:使用JWT会话策略可以避免依赖服务器端存储,令牌直接包含在JWT中,更适配App Router的无状态特性。

内容的提问来源于stack exchange,提问作者Carter Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 20:35:22