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

使用MSAL+NestJS调用Microsoft Graph API /me/todo/lists时遇401未授权错误

NestJS调用Microsoft Graph API获取待办列表报401 UnknownError问题

配置概述

  • 创建MsGraphService,通过@azure/msal-node初始化ConfidentialClientApplication,配置包含clientId、clientSecret和authority;
  • 使用@microsoft/microsoft-graph-client生成Graph客户端,认证提供者静默获取令牌并开启forceRefresh: true确保每次获取新令牌;
  • 通过TodoService调用Microsoft Graph API的/me/todo/lists端点,在GraphQL的TodoResolver中调用getTodos方法。

核心代码

MsGraphService

@Injectable()
export class MsGraphService {
  private readonly msalConfig: Configuration;
  private readonly msalClient: ConfidentialClientApplication;

  public readonly scopes: string[] = [
    'Tasks.Read',
    'Tasks.ReadWrite',
    'User.Read',
    'User.ReadWrite',
    'email',
    'openid',
    'profile',
  ];
  public readonly redirectUri: string;

  constructor(private readonly configService: ConfigService) {
    this.msalConfig = {
      auth: {
        clientId: configService.getOrThrow('OAUTH_CLIENT_ID') || '',
        clientSecret: configService.getOrThrow('OAUTH_CLIENT_SECRET'),
        authority: `https://login.microsoftonline.com/${this.configService.getOrThrow(
          'OAUTH_TENANT_ID',
        )}`,
      },
      system: {
        loggerOptions: {
          loggerCallback(loglevel, message, containsPii) {
            Logger.debug(message, 'MsGraphService');
          },
          piiLoggingEnabled: false,
          logLevel: 3,
        },
      },
    };

    this.msalClient = new ConfidentialClientApplication(this.msalConfig);

    this.redirectUri = this.configService.get(
      'OAUTH_REDIRECT_URI',
      'http://localhost:3000/ms-graph/callback',
    );
  }

  public GraphClient(userId: string): Client {
    const client = Client.init({
      authProvider: async (done) => {
        try {
          const account = await this.msalClient
            .getTokenCache()
            .getAccountByHomeId(userId);

          if (!account) {
            done('account not defined', null);
            return;
          }

          const response = await this.msalClient.acquireTokenSilent({
            scopes: this.scopes,
            account,
            forceRefresh: true
          });

          done(null, response.accessToken);
          Logger.debug(response, 'MsGraphService');
        } catch (err) {
          done(err, null);
        }
      },
    });

    return client;
  }
}

TodoService的getTodos方法

getTodos(userId: string) {
    try {
      return this.msService.GraphClient(userId).api(`/me/todo/lists`);
    } catch (err) {
      Logger.error(err);
    }
}

错误信息

调用时收到Graph API返回的401未授权错误:

{
  statusCode: 401,
  code: 'UnknownError',
  requestId: '97df2091-d1c6-43c9-b576-0c32ac123950',
  date: 2023-08-18T06:28:28.000Z,
  body: '{"code":"UnknownError","message":"","innerError":{"date":"2023-08-18T08:28:28","request-id":"97df2091-d1c6-43c9-b576-0c32ac123950","client-request-id":"a3a4a284-4398-73df-df1d-3e3f47e02268"}}'
}

已确认请求范围包含必要权限,Azure应用注册及权限配置正确,但仍无法定位该UnknownError导致的401错误原因。


排查思路与解决建议

1. 验证Access Token有效性

解析acquireTokenSilent返回的accessToken,重点检查:

  • aud(受众)是否为https://graph.microsoft.com,确保令牌针对Graph API生成;
  • scp字段是否包含Tasks.Read或Tasks.ReadWrite,确认权限已正确纳入;
  • exp(过期时间)是否在当前时间之后,排除令牌过期问题;
  • oid(用户对象ID)是否与传入的userId匹配,确保账户对应正确。

2. 确认账户匹配

打印getAccountByHomeId(userId)返回的account完整信息,检查homeAccountId、username等字段是否属于目标用户。若缓存中无正确账户,会导致令牌无效。

3. 修复API调用逻辑

当前getTodos仅返回请求构建器,未实际发送请求,需添加.get()方法触发API调用:

getTodos(userId: string) {
    try {
      return this.msService.GraphClient(userId).api(`/me/todo/lists`).get();
    } catch (err) {
      Logger.error(err);
    }
}

4. 清理MSAL缓存

即使开启forceRefresh: true,仍可尝试清空令牌缓存,重新获取账户和令牌:

await this.msalClient.getTokenCache().removeAllAccounts();

排查缓存中是否存在过期或无效的账户信息。

5. 确认权限授予状态

在Azure门户应用注册的“API权限”选项卡,检查权限是否已被管理员授予(租户级权限),或用户登录时已同意委托权限。

6. 增加调试日志

  • 在authProvider中打印account对象、acquireTokenSilent完整响应,包括idToken、scopes等细节;
  • 开启Graph客户端调试日志,查看请求头是否正确携带Bearer令牌:
    const client = Client.init({
      authProvider: ...,
      debugLogging: true
    });
    

7. 检查端点与租户配置

  • 确认authority中的租户ID正确,多租户应用需设置为https://login.microsoftonline.com/common或对应租户ID;
  • 尝试调用/me端点测试基础用户信息获取,排查是否为待办列表端点的特定问题。

内容的提问来源于stack exchange,提问作者Aien Saidi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 07:11:02