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

Azure AD+Angular+.NET API调用Graph遇MSAL.UiRequiredException解决

解决AADSTS65001错误:OBO流程调用Graph权限问题

核心原因分析

你遇到的AADSTS65001错误本质是:其他用户尚未对Mail.ReadWrite权限授予同意,且代码未处理MsalUiRequiredException来触发用户同意流程。你本人能正常使用,大概率是因为你作为应用注册所有者或已手动完成权限同意。

具体解决步骤

1. 确认权限配置正确性

  • 检查API应用注册的权限:确保添加的是Microsoft Graph 委托类型的Mail.ReadWrite权限(应用权限适用于无用户上下文的后台场景,OBO流程必须用委托权限)。
  • 验证租户用户同意设置:在Azure AD的「企业应用」→「用户设置」中,确认「用户可以同意应用访问他们的数据吗?」已开启(若关闭,即使权限支持用户同意,也需管理员统一授权)。

2. .NET API代码中处理MsalUiRequiredException

当OBO流程触发权限同意需求时,需捕获MsalUiRequiredException,返回带WWW-Authenticate头的401响应,引导前端触发交互式登录获取用户同意:

using Microsoft.Identity.Web;
using Microsoft.Graph;

public async Task<IActionResult> GenerateDraftEmail()
{
    try
    {
        // 定义需要的Graph权限范围
        var graphScopes = new[] { "Mail.ReadWrite" };
        
        // OBO流程获取Graph访问令牌
        var tokenResult = await _tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(graphScopes);
        
        // 初始化Graph客户端
        var graphClient = new GraphServiceClient(
            new DelegateAuthenticationProvider(req =>
            {
                req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokenResult.AccessToken);
                return Task.CompletedTask;
            }));
        
        // 生成Outlook草稿邮件的业务逻辑
        var draftMessage = new Message
        {
            Subject = "草稿邮件示例",
            Body = new ItemBody { ContentType = BodyType.Text, Content = "邮件内容" }
        };
        var createdDraft = await graphClient.Me.Messages.PostAsync(draftMessage);
        
        return Ok(new { DraftUrl = createdDraft.WebLink });
    }
    catch (MsalUiRequiredException ex)
    {
        // 构建授权挑战,返回401引导前端处理
        var authChallenge = _tokenAcquisition.BuildAuthorizationUrl(graphScopes, ex);
        Response.Headers.Append("WWW-Authenticate", authChallenge);
        return Unauthorized();
    }
}

3. Angular前端处理401触发权限同意

在HTTP拦截器中捕获401响应,调用MSAL的交互式令牌获取方法,让用户完成权限同意后重试请求:

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError, switchMap } from 'rxjs';
import { MsalService } from '@azure/msal-angular';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private msalService: MsalService) {}

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    // 先为请求附加当前访问令牌
    const token = this.msalService.instance.getActiveAccount()?.accessToken;
    if (token) {
      request = request.clone({
        setHeaders: { Authorization: `Bearer ${token}` }
      });
    }

    return next.handle(request).pipe(
      catchError((error: HttpErrorResponse) => {
        // 处理401并触发权限同意
        if (error.status === 401 && error.headers.has('WWW-Authenticate')) {
          return this.msalService.acquireTokenPopup({
            scopes: ['Mail.ReadWrite']
          }).pipe(
            switchMap(() => {
              // 更新令牌后重试请求
              const newToken = this.msalService.instance.getActiveAccount()?.accessToken;
              const newRequest = request.clone({
                setHeaders: { Authorization: `Bearer ${newToken}` }
              });
              return next.handle(newRequest);
            })
          );
        }
        return throwError(() => error);
      })
    );
  }
}

4. 验证用户分配与权限范围

  • 确认API应用注册的「公开API」中,已为Angular应用授予访问权限(即Angular应用的客户端ID已添加到API的授权客户端列表)。
  • 检查用户所属组是否已正确分配到API的企业应用中(Azure AD→企业应用→你的API应用→用户和组)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 13:45:23