SvelteKit+Auth.js集成Azure AD时缺失refresh_token问题
问题:SvelteKit 搭配 Auth.js 集成Azure AD无法获取refresh_token
在使用SvelteKit + Auth.js集成Azure AD时,仅能获取到access_token和id_token,无法拿到refresh_token,导致会话无法长期持久化。根据Azure AD官方文档的OAuth2授权码流说明,refresh_token是该流程的必需项。
以下是当前的配置代码:
import { SvelteKitAuth } from "@auth/sveltekit"; import AzureADProvider from "@auth/core/providers/azure-ad"; import { env as privateEnv } from "$env/dynamic/private"; import { env as publicEnv } from "$env/dynamic/public"; import type { HandleFetch } from "@sveltejs/kit"; async function refreshAccessToken(accessToken) { console.log("refreshAccessToken"); try { const url = `https://login.microsoftonline.com/${privateEnv.AZURE_TENANT_ID}/oauth2/v2.0/token`; const req = await fetch(url, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: `grant_type=refresh_token` + `&client_secret=${privateEnv.AZURE_CLIENT_SECRET}` + `&refresh_token=${accessToken.refreshToken}` + `&client_id=${privateEnv.AZURE_CLIENT_ID}` }); const res = await req.json(); console.log(res); return { ...accessToken, accessToken: res.access_token, accessTokenExpires: Date.now() + res.expires_in * 1000, refreshToken: res.refresh_token ?? accessToken.refreshToken }; } catch (error) { console.log(error); return { ...accessToken, error: "RefreshAccessTokenError" }; } } export const handle = SvelteKitAuth({ providers: [ //@ts-expect-error issue https://github.com/nextauthjs/next-auth/issues/6174 AzureADProvider({ clientId: privateEnv.AZURE_CLIENT_ID, clientSecret: privateEnv.AZURE_CLIENT_SECRET, tenantId: privateEnv.AZURE_TENANT_ID, authorization: { params: { scope: `openid profile email api://${privateEnv.AZURE_CLIENT_ID}/Test` } } }) ], callbacks: { async jwt({ token, account }) { console.log("jwt callback"); console.log(token); // Persist the OAuth access_token to the token right after signin if (account) { console.log("account"); console.log(account); token.accessToken = account.access_token; token.accessTokenExpires = Date.now() + account.expires_in * 1000; } console.log(Date.now() + " " + token.accessTokenExpires); if (Date.now() < token.accessTokenExpires) { return token; } console.log("token expired, refreshing..."); return refreshAccessToken(token); }, async session({ session, token, user }) { // Send properties to the client, like an access_token from a provider. console.log(token); session.accessToken = token.accessToken; console.log("session callback"); console.log(session); return session; } } });
解决方案
1. 添加offline_access权限范围
Azure AD默认不会返回refresh_token,必须显式请求offline_access scope。修改AzureADProvider的authorization配置:
AzureADProvider({ clientId: privateEnv.AZURE_CLIENT_ID, clientSecret: privateEnv.AZURE_CLIENT_SECRET, tenantId: privateEnv.AZURE_TENANT_ID, authorization: { params: { scope: `openid profile email offline_access api://${privateEnv.AZURE_CLIENT_ID}/Test` } } })
2. 在JWT回调中保存refresh_token
当前代码没有将account中的refresh_token存入token,导致后续刷新时无法获取到refresh_token。修改jwt回调:
async jwt({ token, account }) { console.log("jwt callback"); console.log(token); // Persist the OAuth access_token and refresh_token to the token right after signin if (account) { console.log("account"); console.log(account); token.accessToken = account.access_token; token.accessTokenExpires = Date.now() + account.expires_in * 1000; // 新增:保存refresh_token token.refreshToken = account.refresh_token; } console.log(Date.now() + " " + token.accessTokenExpires); if (Date.now() < token.accessTokenExpires) { return token; } console.log("token expired, refreshing..."); return refreshAccessToken(token); }
3. 验证Azure AD应用注册配置
确保你的Azure AD应用注册是Web应用/API类型,并且在"身份验证"设置中不需要勾选"隐式授权和混合流"(授权码流无需此项),同时确认应用权限配置中包含offline_access(添加后需完成管理员同意流程)。
完成以上修改后,重新启动应用,登录后就能在account对象中拿到refresh_token,后续的token刷新逻辑可正常工作,实现会话的长期持久化。
内容的提问来源于stack exchange,提问作者Grum
相关产品推荐
相关产品推荐

