next-auth结合Microsoft Graph API:accessToken获取与续期咨询
解决方案与代码示例
1. 正确获取Microsoft Graph API的Access Token
你不需要使用idp_access_token,应该直接使用accounts表中存储的access_token本身。从你提供的JWT解码内容来看,该token已经包含scp: mail.send权限,说明它是专门用于调用Microsoft Graph API的有效令牌。idp_access_token是身份提供商(此处为live.com)的令牌,仅用于与身份提供商的交互,无法直接调用Graph API。
2. 自动刷新过期的Access Token
next-auth的Azure AD B2C Provider会自动处理令牌刷新逻辑,前提是你满足两个条件:
- 在Azure AD B2C应用配置中启用离线访问权限(
offline_accessscope) - next-auth配置中正确包含该scope
当access_token过期时,next-auth会自动使用refresh_token获取新令牌,并更新accounts表中的记录,无需手动编写刷新逻辑。
完整代码示例
第一步:完善next-auth的Azure AD B2C配置
在[...nextauth].js中,确保scope包含mail.send和offline_access:
import NextAuth from "next-auth" import AzureADB2CProvider from "next-auth/providers/azure-ad-b2c" export default NextAuth({ providers: [ AzureADB2CProvider({ tenantId: process.env.AZURE_AD_B2C_TENANT_ID, clientId: process.env.AZURE_AD_B2C_CLIENT_ID, clientSecret: process.env.AZURE_AD_B2C_CLIENT_SECRET, primaryUserFlow: "B2C_1_signupsignin", authorization: { params: { scope: "openid profile email offline_access https://your-tenant-name.onmicrosoft.com/api/mail.send" // 注意:Graph权限的scope格式需遵循Azure AD B2C要求,格式为https://{tenant}.onmicrosoft.com/{api-id}/{scope} } } }) ], session: { strategy: "jwt" // 使用JWT策略便于令牌管理 }, callbacks: { async jwt({ token, account }) { // 首次登录时保存令牌信息到JWT if (account) { token.accessToken = account.access_token token.refreshToken = account.refresh_token token.expiresAt = account.expires_at * 1000 // 转换为毫秒时间戳 } // 令牌未过期则直接返回,next-auth会自动处理过期刷新 if (Date.now() < token.expiresAt) { return token } return token }, async session({ session, token }) { // 将access_token传入前端session(如需在前端调用API) session.user.accessToken = token.accessToken return session } } })
第二步:封装Microsoft Graph Client工具类
在utils/graphClient.js中创建可复用的Graph客户端:
import { Client } from "@microsoft/microsoft-graph-client" import { TokenCredentialAuthenticationProvider } from "@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials" // 自定义凭证类,传递next-auth获取的accessToken class AccessTokenCredential { constructor(accessToken) { this.accessToken = accessToken } async getToken() { return { token: this.accessToken, expiresOnTimestamp: Date.now() + 3600 * 1000 // 有效期可根据实际token的exp字段调整 } } } export async function createGraphClient(accessToken) { const credential = new AccessTokenCredential(accessToken) const authProvider = new TokenCredentialAuthenticationProvider(credential, { scopes: ["https://graph.microsoft.com/.default"] }) return Client.initWithMiddleware({ authProvider }) }
第三步:在API路由中调用Graph API发送邮件
在pages/api/send-email.js中实现邮件发送逻辑:
import { getServerSession } from "next-auth/next" import { authOptions } from "./auth/[...nextauth]" import { createGraphClient } from "../../utils/graphClient" export default async function handler(req, res) { if (req.method !== "POST") { return res.status(405).json({ message: "Method not allowed" }) } // 获取当前用户的session,包含最新的accessToken const session = await getServerSession(req, res, authOptions) if (!session || !session.user.accessToken) { return res.status(401).json({ message: "Unauthorized" }) } try { const graphClient = await createGraphClient(session.user.accessToken) // 调用Graph API发送邮件 await graphClient.api("/me/sendMail").post({ message: { subject: "邮件营销测试", body: { contentType: "Text", content: "这是一封通过自动化工具发送的测试邮件" }, toRecipients: [ { emailAddress: { address: "recipient@example.com" } } ] }, saveToSentItems: true }) return res.status(200).json({ message: "邮件发送成功" }) } catch (error) { console.error("发送邮件失败:", error) return res.status(500).json({ message: "发送邮件失败", error: error.message }) } }
关键注意事项
- 确保Azure AD B2C的API应用已正确配置Microsoft Graph API的
mail.send权限,并完成管理员同意授权。 offline_accessscope是实现令牌自动刷新的核心,必须包含在next-auth的scope配置中。- 建议使用
getServerSession在服务器端获取令牌,避免在前端暴露敏感的access_token。
内容的提问来源于stack exchange,提问作者Will Despard
相关产品推荐
相关产品推荐

