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
相关产品推荐
相关产品推荐

