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

Azure(Microsoft Graph)委托流中刷新令牌的处理及问题

Outlook日历事件同步项目

项目背景

我正在开发一个需在用户Outlook日历中创建事件的项目,核心需求为将作业检查日期添加到相关用户的日历中,同时支持用户手动创建日历事件。

已实现内容

  • 将用户重定向至Microsoft登录页面
  • 用户登录后,获取该用户的access token
  • 通过getTokenCache获取该用户的refresh token

代码实现

路由

router.get('/login', getAuthCodeUrl);
router.get('/callback', handleCallback);

控制器

const { ConfidentialClientApplication } = require('@azure/msal-node');

// TEST APP
const clientId = '945bf51b-xxxx-c5a83898b4b8';
const clientSecret = '9mV8Q~xxxx.zf9GqLGt95UUJ_bGdcp';

const msalConfig = {
  auth: {
    clientId: clientId,
    authority: `https://login.microsoftonline.com/common`,
    clientSecret: clientSecret,
  },
};

const redirectUri = 'http://localhost:3000/api/callback';

const scopes = [
  'User.Read',
  'Calendars.ReadWrite',
  'offline_access',
  'openid',
  'profile',
];

const cca = new ConfidentialClientApplication(msalConfig);

const getAuthCodeUrl = async (req, res) => {
  const authCodeUrlParameters = {
    scopes,
    redirectUri,
  };

  const authUrl = await cca.getAuthCodeUrl(authCodeUrlParameters);
  console.log('authUrl: ', authUrl);
  res.redirect(authUrl);
};

const handleCallback = async (req, res) => {
  const tokenRequest = {
    scopes,
    code: req.query.code,
    redirectUri,
    accessType: 'offline',
  };

  try {
    const authResult = await cca.acquireTokenByCode(tokenRequest);

    const accessToken = authResult.accessToken;

    const refreshToken = () => {
      const tokenCache = cca.getTokenCache().serialize();
      const refreshTokenObject = JSON.parse(tokenCache).RefreshToken;
      const refreshToken =
        refreshTokenObject[Object.keys(refreshTokenObject)[0]].secret;
      return refreshToken;
    };

    const tokens = {
      accessToken,
      refreshToken: refreshToken(),
    };

    console.log('tokens: ', tokens);

    // Handle token result, store tokens, etc.
    res.send('Authentication successful. You can close this window.');
  } catch (error) {
    console.error('Error obtaining access token:', error);
    res.status(500).send('Error obtaining access token');
  }
};

技术问题解答

1. 在refreshToken函数中未传入任何与用户相关的信息,MSAL如何精准获取该用户而非其他用户的刷新令牌?

你当前的实现仅在单用户测试场景下有效——因为全局的token cache里只有这一个用户的令牌。如果有多个用户登录,直接取缓存中第一个refresh token必然会出错。

MSAL的token cache是按用户(account对象)分区存储的,正确做法是从authResult中拿到当前用户的account对象,再通过该对象精准查询对应的refresh token,示例代码:

const refreshTokens = cca.getTokenCache().getRefreshTokens({ account: authResult.account });
const refreshToken = refreshTokens.length > 0 ? refreshTokens[0].secret : null;

2. 查阅资料得知“access token最长可刷新90天”,是否要求用户每90天必须重新登录一次?

是的。默认情况下,refresh token的有效期为90天,每次用它获取新access token时,通常会返回一个新的refresh token(滚动续期)。但如果连续90天未使用该refresh token,或者用户修改密码、撤销应用权限,refresh token就会失效,此时必须让用户重新登录才能获取新的令牌。

3. 能否无需用户重新登录即可获取新的refresh token?

可以。在使用refresh token获取access token的过程中,MSAL会自动返回新的refresh token(前提是当前refresh token未过期),你只需将新的refresh token替换旧的存储即可。但如果refresh token已过期或被撤销,就必须用户重新登录才能获取新的refresh token。

4. 如何判断access token是否过期?是否需要调用Profile或其他API来检查令牌的过期状态?

不需要调用API检查。access token是JWT格式,内部包含exp字段(过期时间戳),你可以解码JWT拿到该字段,与当前时间戳对比判断是否过期。

另外,MSAL提供了acquireTokenSilent方法,会自动检查缓存中的access token是否有效:如果未过期直接返回,过期则自动用refresh token获取新的access token,失败后才会触发用户重新登录。你也可以用@azure/msal-common中的decodeJwt工具函数解码令牌,快速获取过期时间。


内容的提问来源于stack exchange,提问作者Nikhil Mandaliya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 11:02:50