Nicolas C
Nicolas C

Reputation: 754

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

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: Accessing MS Graph from API on behalf of user currently signed in to separate web client 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 ?

Upvotes: 0

Views: 1494

Answers (2)

Nan Yu
Nan Yu

Reputation: 27578

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("access_token"); . 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 => 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. 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 .

Upvotes: 1

Tony Ju
Tony Ju

Reputation: 15629

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

enter image description here

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

enter image description here

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

Upvotes: 1

Related Questions