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

如何为已登录用户获取Microsoft Graph访问令牌?.NET Core+Angular问题

问题分析与解决方案

首先,咱们先搞清楚为什么cca.Users是空的——你当前的代码是基于服务器端渲染(SSR)应用的示例写的,这类应用里用户是直接在后端完成登录的,所以后端的令牌缓存会关联到用户的服务器会话。但你的场景是Angular SPA + .NET Core Web API,用户登录是在前端完成的,后端并没有维护用户的登录会话,自然找不到用户缓存,所以才会抛出这个异常。

针对你的场景,正确的做法是使用On-Behalf-Of(OBO)流程:前端先获取访问后端API的令牌,后端拿到这个令牌后,以此为“断言”向Microsoft Identity Platform申请代表用户访问Graph的令牌,而不是依赖后端的用户令牌缓存。

一、修复后端获取用户Graph令牌的方法(改用OBO流程)

替换你原来的GetUserAccessTokenAsync方法,改成以下实现:

using Microsoft.Identity.Client;
using System.Net.Http.Headers;
using System.Threading.Tasks;

public async Task<string> GetUserAccessTokenAsync(string userAccessToken)
{
    var cca = new ConfidentialClientApplication(
        _clientId, 
        _clientAuthority, 
        _redirectUri, 
        _credential, 
        null, 
        null);

    try
    {
        // 使用OBO流程,以前端传来的用户令牌作为断言
        var result = await cca.AcquireTokenOnBehalfOfAsync(
            _scopes, 
            new UserAssertion(userAccessToken));
        
        return result.AccessToken;
    }
    catch (MsalException ex)
    {
        throw new ServiceException(new Error
        {
            Code = GraphErrorCode.AuthenticationFailure.ToString(),
            Message = $"OBO流程获取令牌失败: {ex.Message}"
        });
    }
}

关键说明:

  • 你需要从后端API的请求头中提取前端传来的用户令牌(Authorization: Bearer <token>),然后把这个令牌传入该方法。
  • 确保你的Azure AD应用注册中,已经给后端API添加了委派权限(比如Graph的User.Read等),并完成了管理员同意。
  • 要在后端API的配置中启用JWT验证,确保前端传来的令牌是有效的。

二、Angular前端调用后端API的示例(含令牌传递)

假设你用MSAL.js(Angular 5对应msal-angular v1.x)完成前端登录,登录后获取访问后端API的令牌,然后在调用后端时把令牌放在请求头里:

import { HttpClient, HttpHeaders } from '@angular/common/http';
import { MsalService } from '@azure/msal-angular';

@Component({ /* 组件配置 */ })
export class YourComponent {
  constructor(private http: HttpClient, private msalService: MsalService) {}

  async callBackendAndGraph() {
    // 获取访问后端API的令牌
    const accessToken = await this.msalService.acquireTokenSilent({
      scopes: ["api://<你的后端API应用ID>/access_as_user"] // 后端API的权限范围
    });

    // 构建请求头,携带令牌
    const headers = new HttpHeaders({
      'Authorization': `Bearer ${accessToken}`
    });

    // 调用后端API
    const response = await this.http.get('/api/graph/user', { headers }).toPromise();
    console.log(response);
  }
}

三、后端API的令牌验证配置

在Startup.cs中,确保正确配置Azure AD认证,验证前端传来的令牌:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.Authority = $"https://login.microsoftonline.com/{_tenantId}/v2.0";
            options.Audience = _clientId; // 后端API的应用ID
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidIssuer = $"https://login.microsoftonline.com/{_tenantId}/v2.0"
            };
        });

    // 其他服务配置...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // 其他中间件...
    app.UseAuthentication();
    app.UseMvc();
}

四、为什么原来的方法不适用?

你参考的aspnetcore-connect示例是给服务器端应用设计的:用户通过后端的登录页面完成认证,后端会把用户的令牌缓存到Session中,所以cca.Users能找到用户。但你的SPA前端是在客户端完成登录,后端没有用户的登录会话,自然无法从缓存中找到用户数据,这就是问题的核心。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:50:14