NextAuth.js集成Keycloak与自定义基础路径时登出遇CSRF请求失败错误
NextAuth.js(v4.24.11)集成Keycloak自定义基础路径下登出报错:Failed to fetch /api/auth/csrf
问题描述
在使用NextAuth.js(v4.24.11)集成Keycloak并配置自定义基础路径时,执行登出操作出现控制台错误:Failed to fetch /api/auth/csrf
已尝试的排查步骤
- 确认.env中的NEXTAUTH_URL已正确设置为匹配自定义基础路径
- 手动验证csrf端点,发现该端点无法访问
- 设置NEXTAUTH_DEBUG=true以获取更多日志,但未得到有效信息
- 添加NODE_TLS_REJECT_UNAUTHORIZED=0用于本地测试,规避TLS问题
- 确保所有路由都已正确添加基础路径前缀(/workflow-checkmate)
环境配置(ENV)
NEXTAUTH_URL=http://localhost:3000/workflow-checkmate/api/auth
错误详情
浏览器端错误截图


终端端错误截图

认证选项(Auth Options)代码
import { KEYCLOAK_CLIENT_ISSUER, KEYCLOAK_CLIENT_SECRET, keycloakClientId, NEXTAUTH_SECRET, } from "@/constants"; import { jwtDecode } from "jwt-decode"; import { NextAuthOptions } from "next-auth"; import KeycloakProvider from "next-auth/providers/keycloak"; import { refreshAccessToken } from "./utils/refreshAccessToken"; import { encrypt } from "./utils/encrypt-decrypt-auth"; export const authOptions: NextAuthOptions = { session: { strategy: "jwt", }, secret: NEXTAUTH_SECRET, // PROVIDERS providers: [ KeycloakProvider({ clientId: keycloakClientId, clientSecret: KEYCLOAK_CLIENT_SECRET, issuer: KEYCLOAK_CLIENT_ISSUER, }), ], callbacks: { async redirect({ url, baseUrl }) { return url.startsWith(baseUrl)? url : baseUrl; }, async jwt({ token, account }) { const nowTimeStamp = Math.floor(Date.now() / 1000); if (account) { token.decoded = jwtDecode(account.access_token?? ""); token.access_token = account.access_token?? ""; token.id_token = account.id_token?? ""; token.expires_at = account.expires_at?? 0; token.refresh_token = account.refresh_token?? ""; return token; } else if (nowTimeStamp < token.expires_at) { return token; } else { try { console.log("Refreshing access token"); const refreshedToken = await refreshAccessToken(token); console.log("Access token refreshed"); return refreshedToken; } catch (error) { console.error("Error refreshing access token", error); return {...token, error: "RefreshAccessTokenError" }; } } }, async session({ session, token }) { session.access_token = encrypt(token.access_token); session.id_token = encrypt(token.id_token); session.error = token.error; session.user.id = token.sub; session.user.name = token.name; session.user.email = token.email; return session; }, }, // DEBUG debug: true, logger: { error(code, metadata) { console.log(code, metadata); }, warn(code) { console.log(code); }, debug(code, metadata) { console.log(code, metadata); }, }, };
会话提供者(SESSION PROVIDER)代码
"use client"; import { Session } from "next-auth"; import { SessionProvider } from "next-auth/react"; import React from "react"; type props = { children: React.ReactNode; session?: Session | null; }; export default function SessionProviderWrapper({ children, session }: props) { return ( <SessionProvider session={session} basePath={`workflow-checkmate/api/auth`}> {children} </SessionProvider> ); }
内容的提问来源于stack exchange,提问作者NoobMaster69
相关产品推荐
相关产品推荐

