调用 ASP.Net Core API 代表来自单页应用的用户的 Graph API。

huangapple go评论69阅读模式
英文:

Calling Graph API from ASP.Net Core API on behalf of user from SPA

问题

I'm here to provide translations for the non-code parts of your text. Here's the translated text:

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

我需要代表已连接的用户调用Graph API。我看到了这个问题:https://stackoverflow.com/questions/53732255/accessing-ms-graph-from-api-on-behalf-of-user-currently-signed-in-to-separate-we/53733028,它看起来真的像我要实现的解决方案。

我试图在这里复制所提出的解决方案:https://joonasw.net/view/azure-ad-on-behalf-of-aspnet-core。我理解我需要配置JwtBearer设置,但如果我已经有AddAzureADBearer,我该如何调用.AddJwtBearer呢?AddAzureADBearer本身调用AddJwtBearer(请参阅此处)。

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

我不太理解在问题这里提出的解决方案。

更新

多亏了Tony Ju的回答,我已经能够进行身份验证。但是,当我调用Graph API时,我收到Authorization_RequestDenied权限不足以完成操作的错误。

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

我使用以下代码获取令牌:

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,但没有成功。Graph API请求所需的范围应该在初始用户登录中吗,还是在代表用户请求令牌时扩展范围?

英文:

I'm building an API which is called by a SPA (angular).
The user is authenticated using Azure AD and the API uses AzureAdBearer for its authentication.

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

I need to call Graph API and would like to do it on behalf of the connected user.
I have seen this question: https://stackoverflow.com/questions/53732255/accessing-ms-graph-from-api-on-behalf-of-user-currently-signed-in-to-separate-we/53733028 which really looks like the solution I try to implement.
I try to replicate this proposed solution here: https://joonasw.net/view/azure-ad-on-behalf-of-aspnet-core
As I understand it I need to configure the JwtBearer settings, but how can I call the .AddJwtBearer if I already have .AddAzureADBearer.
AddAzureADBearer itself calls the AddJwtBearer (cf. here)

I created a secret in the app registration of my API and used it to implement a IAuthenticationProvider to get the token on behalf of the user, but I get an error AADSTS65001.
I guess this is because I'm missing the AddJwtBearer configuration to save the user's token in cache, but I'm not really sure.

I don't really understand the solution proposed on the question here.

UPDATE

Thanks to Tony Ju's answer I have been able to authenticate. But when I call graph api I get Authorization_RequestDenied Insufficient privileges to complete the operation.

I don't know why but can't manage to configure Fiddler properly to capture the request to Azure AD. I enabled ADAL logs and got this

[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

I used this code to get the token:

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);

I tried to rewrite it for MSAL instead of ADAL but didn't success.
Should the scope needed for the graph api request should be in the initial user login or do we expand the scope in the on-behalf-of user request token ?

答案1

得分: 1

请检查您的 Web API 权限。转到 应用程序注册->找到您的 Web API 应用程序->权限

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

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

https://login.microsoftonline.com/{租户}/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
英文:

Please check the permissions of your web api permissions. Go to App registrations->find your web api app->permissions.

调用 ASP.Net Core API 代表来自单页应用的用户的 Graph API。

If you have permissions which need admin consent, you need to grant admin consent for your tenant.

调用 ASP.Net Core API 代表来自单页应用的用户的 Graph API。

If you don't have any permissions which need admin consent, try to grant user consent by sending a request like the following example

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

答案2

得分: 1

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

你可以在IAuthenticationProvider中设置断点,以确认是否通过string token = await httpContext.GetTokenAsync("access_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("断点");
        },
    };
});
  1. 直接使用AddJwtBearer扩展。

如果使用Azure AD v1.0,你可以点击这里查看如何配置On-Behalf-Of流应用程序,包括如何配置已知的客户端应用程序。比较你的应用程序发送的请求与文档显示的内容。如果使用Azure AD v2.0,你可以查看此文档

英文:

> I guess this is because I'm missing the AddJwtBearer configuration to save the user's token in cache, but I'm not really sure.

You can simply set breakpoint in IAuthenticationProvider to confirm whether it gives token via string token = await httpContext.GetTokenAsync(&quot;access_token&quot;); . I think it should be there , if there is no access token , you can try below ways :

  1. Override the JwtBearerOptions :

     services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
                 .AddAzureADBearer(options =&gt; Configuration.Bind(&quot;AzureAd&quot;, options));
    
     services.Configure&lt;JwtBearerOptions&gt;(AzureADDefaults.JwtBearerAuthenticationScheme, options =&gt;
     {
         options.SaveToken = true;
         options.Events = new JwtBearerEvents
         {
             OnMessageReceived = async (ctx) =&gt;
             {
                 Console.WriteLine(ctx.Token);
             },
    
             OnTokenValidated = async (ctx) =&gt;
             {
                 Console.WriteLine(&quot;BreakPoint&quot;);
             },
         };
     });
    
  2. Directly use AddJwtBearer extension .

If using Azure AD v1.0 , you can click here for how to config the On-Behalf-Of flow applications include how to configure known client applications . Compare the request your application sent with the document shows . If using Azure AD v2.0 , you can check this document .

huangapple
  • 本文由 发表于 2020年1月4日 00:20:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/59581880.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定