NextAuth Credential Provider对接Azure后端的令牌存储与使用问题
问题描述
我是Next.js和NextAuth新手,目前我们已有部署在Azure上的.NET后端,正计划从React前端迁移至Next.js。我尝试使用CredentialsProvider实现认证,通过Axios调用Azure后端获取了access_token、refresh_token、endpoint_tokens等所需令牌,但不清楚如何让这些令牌在NextAuth体系中生效。此外,每次调用Azure后端都需要携带这些令牌,由于服务端无法操作localStorage,我想询问是否可以通过修改Session来存储这些令牌。
相关代码
export const authProviders: NextAuthOptions = { providers: [ GoogleProvider({ clientId: process.env.GOOGLE_CLIENTID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, }), CredentialsProvider({ name: "Credentials", credentials: { username: { label: "Username", type: "text", placeholder: "user", value: "Administrator", }, password: { label: "Password", type: "password", placeholder: "pass", value: "password", }, }, async authorize(credentials, req) { if (!credentials || !credentials?.username || !credentials?.password) { return null; } try { const { data, cookies } = await createBrowserToken( credentials.username, credentials.password ); if (data) { console.log("response for tokens", data); const { access_token, refresh_token, expires_in, endpoint_tokens } = data; const getUserToken = endpoint_tokens.find( (token: any) => token.endpoint_route === "GETapi/v1/Users/GetUser" ).access_token; console.log( "response tokensss", getUserToken, access_token, cookies ); if (cookies !== null) { const user = await fetchUser(getUserToken, access_token, cookies); console.log("user", user); if (user) { return { id: user.id, name: `${user.firstName}-${user.lastName}`, email: user.email, }; } else return null; } return null; } else { return null; } } catch (error) { console.error("Error during login:", error); return null; } } }) ] }
解决方案
1. 在Session中存储自定义令牌
完全可以通过NextAuth的Session存储这些令牌,核心是修改jwt和session两个回调:
jwt回调:用户认证成功后,将令牌附加到JWT对象(NextAuth默认用JWT保存会话数据,服务端/客户端均可访问)session回调:把JWT中的令牌映射到Session对象,让前端能通过useSession获取
修改你的authProviders配置,添加以下回调:
export const authProviders: NextAuthOptions = { providers: [ // 保留原有GoogleProvider和CredentialsProvider配置 ], callbacks: { async jwt({ token, user }) { // 首次登录时注入令牌数据 if (user) { token.access_token = user.access_token; token.refresh_token = user.refresh_token; token.endpoint_tokens = user.endpoint_tokens; token.expires_in = user.expires_in; } return token; }, async session({ session, token }) { // 将JWT中的令牌同步到Session session.user.access_token = token.access_token as string; session.user.refresh_token = token.refresh_token as string; session.user.endpoint_tokens = token.endpoint_tokens as any[]; session.user.expires_in = token.expires_in as number; return session; } }, // 显式声明Session策略为JWT(默认就是JWT,可选) session: { strategy: "jwt" } }
2. 修改authorize方法返回值
在authorize中把令牌和用户信息一起返回,这样jwt回调才能拿到令牌数据:
async authorize(credentials, req) { if (!credentials || !credentials?.username || !credentials?.password) { return null; } try { const { data, cookies } = await createBrowserToken( credentials.username, credentials.password ); if (data) { const { access_token, refresh_token, expires_in, endpoint_tokens } = data; const getUserToken = endpoint_tokens.find( (token: any) => token.endpoint_route === "GETapi/v1/Users/GetUser" ).access_token; if (cookies !== null) { const user = await fetchUser(getUserToken, access_token, cookies); if (user) { // 返回用户信息+所有令牌数据 return { id: user.id, name: `${user.firstName}-${user.lastName}`, email: user.email, access_token, refresh_token, expires_in, endpoint_tokens }; } else return null; } return null; } else { return null; } } catch (error) { console.error("Error during login:", error); return null; } }
3. 在API调用中使用令牌
- 客户端组件:通过
useSession获取令牌,携带到请求头:
import { useSession } from "next-auth/react"; const { data: session } = useSession(); // 调用Azure后端 axios.get("https://your-azure-backend/api/endpoint", { headers: { Authorization: `Bearer ${session?.user.access_token}` } });
- 服务端组件/API路由:通过
getServerSession获取令牌(服务端无法用useSession):
import { getServerSession } from "next-auth/next"; import { authProviders } from "@/path-to-your-auth-config"; export async function GET() { const session = await getServerSession(authProviders); if (!session) { return new Response("Unauthorized", { status: 401 }); } const response = await fetch("https://your-azure-backend/api/endpoint", { headers: { Authorization: `Bearer ${session.user.access_token}` } }); const data = await response.json(); return Response.json(data); }
4. 自动刷新令牌(可选)
在jwt回调中检查令牌过期时间,自动调用后端刷新接口更新令牌:
async jwt({ token, user }) { // 首次登录注入令牌和过期时间戳 if (user) { token.access_token = user.access_token; token.refresh_token = user.refresh_token; token.expires_at = Date.now() + user.expires_in * 1000; token.endpoint_tokens = user.endpoint_tokens; return token; } // 令牌未过期直接返回 if (Date.now() < token.expires_at) { return token; } // 令牌过期,调用刷新接口 try { const response = await axios.post("https://your-azure-backend/refresh-token", { refresh_token: token.refresh_token }); const { access_token, expires_in, endpoint_tokens } = response.data; return { ...token, access_token, expires_at: Date.now() + expires_in * 1000, endpoint_tokens }; } catch (error) { // 刷新失败,标记错误触发重新登录 console.error("Token refresh failed:", error); return { ...token, error: "RefreshTokenError" }; } }
内容的提问来源于stack exchange,提问作者Philomath
相关产品推荐
相关产品推荐

