You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Next.js+NextAuth.js中安全处理Azure AD访问令牌的疑问

关于NextAuth.js + Azure AD认证中Access Token的安全性问题

问题描述

我正在开发一个Next.js应用,使用NextAuth.js实现Azure AD认证。认证成功后获取access token,用于调用Microsoft Graph API。但发现该访问令牌在React Dev Tools的React上下文中可见,以下是我的简化实现代码:

NextAuth配置代码

import NextAuth, { NextAuthOptions } from 'next-auth'
import AzureADProvider from 'next-auth/providers/azure-ad'

export const authOptions: NextAuthOptions = {
  // Configure one or more authentication providers
  providers: [
    AzureADProvider({
      clientId: process.env.AZURE_AD_CLIENT_ID!,
      clientSecret: process.env.AZURE_AD_CLIENT_SECRET!,
      tenantId: process.env.AZURE_AD_TENANT_ID!,
      authorization: { 
        params: { scope: 'openid profile user.Read email' },
        url: `https://login.microsoftonline.com/${process.env.AZURE_AD_TENANT_ID!}/oauth2/v2.0/authorize`
      },
      token: { 
        params: { scope: 'openid profile user.Read email' },
        url: `https://login.microsoftonline.com/${process.env.AZURE_AD_TENANT_ID!}/oauth2/v2.0/token`
      },
    })
  ],
  callbacks: {
    async jwt({ token, user, account, profile }) {
      // Persist the OAuth access_token to the token right after signin
      console.log("JWT token", token);

      if (account) {
        token.accessToken = account.access_token;
      }
      return token;
    },

    async session({ session, token, user }) {
      // Send properties to the client, like an access_token and user id from a provider.
      console.log("Session token", token);
      if (token) {
        session.accessToken = token.accessToken;
      }
      return session
    }
    
  }
}

export default NextAuth(authOptions)

前端组件调用代码

import { useSession } from 'next-auth/react'

export default function Component() {
  const { data: session } = useSession();
  console.log("accesstoken", session.accessToken);

  // I then use session.accessToken to make authenticated calls to the Microsoft Graph API.
}

尽管我知道令牌是短期且用户会话专属的,但仍担心其包含的敏感信息在React上下文中可见。请问当前方案是否安全?是否需要担忧令牌的可见性?若存在安全风险,有哪些更安全的令牌存储管理替代策略?另外我曾考虑用NextAuth适配器将用户数据存入数据库,需要时从中获取access token,但觉得该方案不佳,想听听您的看法。


回答

一、当前方案的安全性判断

当前方案符合OAuth2/OpenID Connect常规安全规范,无需过度担忧令牌的可见性,原因如下:

  • 令牌特性:Azure AD的access token默认有效期仅1小时,到期自动失效,风险窗口有限;且令牌权限是你配置的user.Read email这类限定范围,不会出现超权限操作。
  • 可见范围:令牌仅在当前用户的浏览器上下文可见(包括React Dev Tools),其他用户无法获取,除非用户主动泄露(如分享屏幕、导出Dev Tools数据)。
  • 必要性:要在前端直接调用Graph API,必然需要让前端持有access token,这是这类场景的常规实现方式。

当然,如果你的业务涉及高度敏感数据,对令牌泄露风险有极致要求,可以考虑更安全的方案。

二、更安全的替代策略

1. 后端代理Graph API请求(最推荐)

核心思路是不在前端暴露任何access token,所有Graph API请求都通过自己的Next.js后端接口转发:

  • 保留现有jwt callback逻辑,将access token持久化到后端的JWT中,但移除session callback里传递access token到前端的代码。
  • 创建Next.js API路由作为代理,比如/api/graph/me:
import { getServerSession } from "next-auth/next";
import { authOptions } from "../auth/[...nextauth]";
import axios from "axios";

export default async function handler(req, res) {
  const session = await getServerSession(req, res, authOptions);
  if (!session) {
    return res.status(401).json({ error: "Unauthorized" });
  }

  // 从后端JWT中提取access token
  const accessToken = session.token.accessToken;
  try {
    const graphRes = await axios.get("https://graph.microsoft.com/v1.0/me", {
      headers: { Authorization: `Bearer ${accessToken}` }
    });
    res.status(200).json(graphRes.data);
  } catch (err) {
    res.status(err.response?.status || 500).json({ error: "Graph API request failed" });
  }
}
  • 前端直接调用自己的API路由,无需接触access token。

这种方案彻底避免了前端令牌泄露的风险,还能在后端统一处理令牌刷新、权限校验等逻辑。

2. 仅在后端存储令牌(不传递到前端)

如果不需要前端直接调用Graph API,完全可以把access token只存在后端的JWT或数据库中,前端仅通过session状态确认用户身份,所有需要令牌的操作都通过后端完成。

三、关于NextAuth适配器存储令牌的看法

用NextAuth适配器将access token存入数据库是可行的,但要结合场景判断:

  • 适用场景:需要后台定时任务(如同步用户数据)、多服务共享令牌的场景,持久化存储能方便后端随时获取令牌。
  • 注意事项:需确保令牌在数据库中加密存储,同时要处理令牌的过期、刷新和清理逻辑,避免无效数据堆积。
  • 对比后端代理:如果只是前端需要调用Graph API,后端代理方案更轻量,不需要额外的数据库操作,没必要用适配器存储。

内容的提问来源于stack exchange,提问作者Leonardo Muniz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 12:16:04