【问题标题】:Calling Graph API from ASP.Net Core API on behalf of user from SPA代表 SPA 中的用户从 ASP.Net Core API 调用 Graph API
【发布时间】:2024-05-19 06:50:02
【问题描述】:

我正在构建一个由 SPA(角度)调用的 API。 用户使用 Azure AD 进行身份验证,API 使用 AzureAdBearer 进行身份验证。

public void ConfigureServices(IServiceCollection services)
{
 [...]
    services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
            .AddAzureADBearer(options => Configuration.Bind("AzureActiveDirectory", options));
 [...]
}

我需要调用 Graph API 并希望代表已连接的用户执行此操作。 我已经看到了这个问题:Accessing MS Graph from API on behalf of user currently signed in to separate web client 这看起来真的很像我尝试实施的解决方案。 我尝试在这里复制这个建议的解决方案:https://joonasw.net/view/azure-ad-on-behalf-of-aspnet-core 据我了解,我需要配置 JwtBearer 设置,但如果我已经拥有.AddAzureADBearer,我该如何调用.AddJwtBearer。 AddAzureADBearer 本身调用 AddJwtBearer(参见here

我在我的 API 的应用注册中创建了一个秘密,并使用它来实现 IAuthenticationProvider 以代表用户获取令牌,但我收到错误 AADSTS65001。 我想这是因为我缺少将用户的令牌保存在缓存中的 AddJwtBearer 配置,但我不太确定。

我不太明白here这个问题上提出的解决方案。

更新

感谢Tony Ju's answer 我已经能够进行身份验证。但是当我调用图形 api 时,我得到 Authorization_RequestDenied 权限不足,无法完成操作。

我不知道为什么,但无法正确配置 Fiddler 以捕获对 Azure AD 的请求。我启用了 ADAL 日志并得到了这个

[Information][False]: 2020-01-09T09:11:12.1104926Z: AdalLoggerBase.cs: ADAL PCL.CoreCLR with assembly version '5.2.4.0', file version '5.2.4.0' and informational version '5.2.4' is running...
[Information][True]: 2020-01-09T09:11:12.1183853Z: AdalLoggerBase.cs: === Token Acquisition started:
Authority: https://login.microsoftonline.com/d6397071-8e3e-45d2-a2d6-36698acf0fea/
Resource: https://graph.microsoft.com
ClientId: [...]
CacheType: Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache (0 items)
Authentication Target: User

[Verbose][False]: 2020-01-09T09:11:12.1395851Z: AdalLoggerBase.cs: Username provided in user assertion - False
[Verbose][False]: 2020-01-09T09:11:12.5277838Z: AdalLoggerBase.cs: Loading from cache.
[Verbose][False]: 2020-01-09T09:11:12.5395969Z: AdalLoggerBase.cs: Looking up cache for a token...
[Information][False]: 2020-01-09T09:11:12.5467711Z: AdalLoggerBase.cs: No matching token was found in the cache
[Information][False]: 2020-01-09T09:11:12.5706903Z: AdalLoggerBase.cs: No matching token was found in the cache
[Verbose][False]: 2020-01-09T09:11:12.5736362Z: AdalLoggerBase.cs: Looking up cache for a token...
[Information][False]: 2020-01-09T09:11:12.5764248Z: AdalLoggerBase.cs: No matching token was found in the cache
[Verbose][False]: 2020-01-09T09:11:12.5804061Z: AdalLoggerBase.cs: Either a token was not found or an exception was thrown.
[Verbose][False]: 2020-01-09T09:11:12.5844932Z: AdalLoggerBase.cs: Cannot invoke the broker directly, may require install ...
[Verbose][False]: 2020-01-09T09:11:12.5891021Z: AdalLoggerBase.cs: Check and AcquireToken using broker
[Verbose][False]: 2020-01-09T09:11:12.5919785Z: AdalLoggerBase.cs: Broker invocation is NOT required
[Verbose][False]: 2020-01-09T09:11:12.9510499Z: AdalLoggerBase.cs: Storing token in the cache...
[Verbose][False]: 2020-01-09T09:11:12.9583491Z: AdalLoggerBase.cs: An item was stored in the cache
[Information][True]: 2020-01-09T09:11:12.9917987Z: AdalLoggerBase.cs: === Token Acquisition finished successfully. An access token was returned: Expiration Time: 09/01/2020 10:10:54 +00:00Access Token Hash: GtaeLmKsVSj82umwxVcgghW3/X/N2hKhaxyb7XBbBU0=
 User id: 3b224748-42c5-406d-a0c0-e9c9f5238361

我使用此代码获取令牌:

var userAssertion = new UserAssertion(token, "urn:ietf:params:oauth:grant-type:jwt-bearer", user.upn);
var authContext = new AuthenticationContext(_aadOptions.Authority);
var clientCredential = new ClientCredential(_aadOptions.ClientId, _aadOptions.ClientSecret);

var result = await authContext.AcquireTokenAsync("https://graph.microsoft.com", clientCredential, userAssertion);

我尝试为 MSAL 而不是 ADAL 重写它,但没有成功。 图 api 请求所需的范围应该在初始用户登录中还是我们在代表用户请求令牌中扩展范围?

【问题讨论】:

  • 详细的错误信息是什么? AADSTS65001 还不够。

标签: asp.net-core oauth-2.0 azure-active-directory


【解决方案1】:

请检查您的 web api 权限的权限。转到应用注册->找到您的 Web api 应用->权限

如果您拥有需要管理员同意的权限,则需要为您的租户授予管理员同意。

如果您没有任何需要管理员同意的权限,请尝试通过发送如下示例的请求来授予用户同意

https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=cbc32712-****-4532-802d-303998a6e712
&response_type=code
&redirect_uri=http://localhost
&response_mode=query
&scope=https://graph.microsoft.com
&state=12345

【讨论】:

  • 感谢您的输入,我的权限不需要管理员同意(User.ReadBasic.All),所以我使用最后一个请求来授予用户同意。现在我成功地从 Azure AD 获得了一个令牌,但是当我调用 graph api 时,我得到了 Authorization_RequestDenied Insufficient 权限来完成操作。我可能需要使用网络数据包分析器检查 AcquireTokenAsync 请求,以检查是否为代表请求正确设置了所有范围和参数。
  • 我会将其标记为答案,因为它解决了我的身份验证问题。我创建了另一个问题here,因为缺少的特权也得到了回答。 谢谢
【解决方案2】:

我猜这是因为我缺少将用户令牌保存在缓存中的 AddJwtBearer 配置,但我不太确定。

您可以简单地在IAuthenticationProvider 中设置断点来确认它是否通过string token = await httpContext.GetTokenAsync("access_token"); 给出token。我认为它应该在那里,如果没有访问令牌,您可以尝试以下方式:

  1. 覆盖 JwtBearerOptions :

    services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
                .AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
    
    services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
    {
        options.SaveToken = true;
        options.Events = new JwtBearerEvents
        {
            OnMessageReceived = async (ctx) =>
            {
                Console.WriteLine(ctx.Token);
            },
    
            OnTokenValidated = async (ctx) =>
            {
                Console.WriteLine("BreakPoint");
            },
        };
    });
    
  2. 直接使用AddJwtBearer扩展名。

如果使用Azure AD v1.0,您可以点击here了解如何配置代理流应用程序,包括如何配置已知客户端应用程序。将您的应用程序发送的请求与文档显示的请求进行比较。如果使用 Azure AD v2.0,可以查看this document

【讨论】:

  • 感谢输入,httpContext.GetTokenAsync("access_token");成功让我得到用户的令牌,所以我的猜测是错误的。无论如何,感谢您向我展示如何覆盖 JwtBearerAuthenticationScheme。
  • 好吧,然后将您的应用程序发送的请求与文档显示进行比较,并向我们显示详细的错误消息。