1

I'm trying to create an Angular 10 front end that accesses a .NET Core 3.1 Web Api back end. Both of them secured under Azure AD using the MSAL Libraries. I created and SPA app usign Visual Studio 2019's SPA Angular Template for Web Apps. On the client app, I'm using msal-angular to log in and get an access token. I followed both the Quickstart, the Tutorial and finally I've tried withe the Single Page App Scenario (using the implicit flow). For the Web API, I followed what is explained in the Single Page App Scenario and also I've tried with the Protected web API Scenario and Web App that calls web APIs Scenario, but when I try to access any Web API endpoint that has an Authorize decorator, I get the following error: System.UnauthorizedAccessException: IDW10201: Neither scope or roles claim was found in the bearer token.

Anyone else has successfully created something like what I'm trying to do (SPA template, Angular 10, .NET Core 3.1 web API)? What am I doing wrong or missing?

For the purposes of my question, my apps registration data are as follows:

Tenant:
Tenant Id: 'tttttttt-tttt-tttt-tttt-ttttttttttt'
Domain: 'notrealdomain.onmicrosoft.com'

Client App:
Client Id: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'

API App:
Client Id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
App Id: 'api://xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/access_as_user'

My token looks something like the following:

{
  "aud": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
  "iss": "https://login.microsoftonline.com/tttttttt-tttt-tttt-tttt-ttttttttttt/v2.0",
  "iat": 1595531437,
  "nbf": 1595531437,
  "exp": 1595535337,
  "aio": "ATQAy/8QAA01PbjYzolbeAAE6rlHrUQ5ndpD/CDAxneSI8BsXSdm3Sl7CBYWqoS+jyBUNtJ/Ovwe",
  "groups": [
    "gggggggg-gggg-gggg-gggg-gggggggggggg",
    "ffffffff-ffff-ffff-ffff-ffffffffffff"
  ],
  "name": "John Doe",
  "nonce": "hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh",
  "oid": "iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii",
  "preferred_username": "john@notrealdomain.onmicrosoft.com",
  "sub": "hrhuh3UtYWRmuT-JnNWuFhFYvWjbJFmegz7k2ay0RbI",
  "tid": "tttttttt-tttt-tttt-tttt-ttttttttttt",
  "uti": "PAkaSoPlaWi_vBYklUKtAA",
  "ver": "2.0"
}

app.module.ts

 @NgModule({
 declarations: [
 ... code ommited ...
 ],
 imports: [
 ... code ommited ...
 MsalModule.forRoot({
        auth: {
          clientId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
          authority: 'https://login.microsoftonline.com/tttttttt-tttt-tttt-tttt-ttttttttttt/',
          postLogoutRedirectUri: "https://localhost:44000/signout",
          redirectUri: "https://localhost:44000/",
        },
        cache: {
          cacheLocation: 'localStorage',
          storeAuthStateInCookie: isIE, // Set to true for Internet Explorer 11
        },
        framework: {
            unprotectedResources: ['https://localhost:44000/api/Unsecured'],
            protectedResourceMap: new Map([
              ['https://localhost:44375', ['api://xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/access_as_user']],
              ['https://graph.microsoft.com/v1.0/me', ['user.read']],
              ['api://xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', ['access_as_user']],
            ])
        },
      }, {
        popUp: false,
        consentScopes: [
            'user.read',
            'openid',
            'profile',
            'api://xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/access_as_user',
        ],
        extraQueryParameters: {}
      }),
 ... code ommited ...
 providers: [
     ... code ommited ...
     {
       provide: HTTP_INTERCEPTORS,
       useClass: MsalInterceptor,
       multi: true
     },

I'm logging in using the following method in one of my components where this.msalService is injected as private msalService: MsalService :

this.msalService.loginRedirect({
  scopes: [
      'api://xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/access_as_user',
    ]
});

On my Web API, I have the following configuration on my appsettings.json file:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "notrealdomain.onmicrosoft.com",
    "TenantId": "tttttttt-tttt-tttt-tttt-ttttttttttt",
    "ClientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "Audience": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
  },
... code ommited ...
}

And finally, my StartUp.cs looks like this:

    public void ConfigureServices(IServiceCollection services)
    {
        // Autenticación
        services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAd");
        ... code ommited ...
    }
 
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        ... code ommited ...
        app.UseRouting();

        // Autorización
        app.UseAuthentication();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller}/{action=Index}/{id?}");
        });

        app.UseSpa(spa =>
        ... code ommited ...
    } 
flaria
  • 195
  • 1
  • 6
  • 14

1 Answers1

0

Make sure you've granted the permission to your Azure AD Application to read user data:

enter image description here

Also, in the manifest set the following settings:

enter image description here

Thiago Custodio
  • 17,332
  • 6
  • 45
  • 90
  • Thanks, Thiago. To which app should I add this settings: The Client or the API? On my Client App Registration I've got "groupMembershipClaims": "All, ApplicationGroup", and "oauth2AllowIdTokenImplicitFlow": true, "oauth2AllowImplicitFlow": true. – flaria Jul 23 '20 at 20:26
  • Thiago, I've checked on both web API and Client the manifests and I've changed the settings to reflect your suggestion, but no luck. Do you have another suggestion? – flaria Jul 23 '20 at 21:59
  • I've did this many years ago and I remember this was the way. Check the JWT token if the there's a claims with the roles/groups. Otherwise, there's a high chance the token is being truncated and you'll need to query azure ad manually – Thiago Custodio Jul 24 '20 at 13:34
  • Thanks, Thiago. Chances are things have changed. The Microsoft.Identity.Web library I'm using on the web API side is relatively new (just released early 2020). It replaced what used to be done using Microsoft.AspNetCore.AzureAD.UI. But I'll try reading the JWT manually and querying azure ad from there. Hopefully someone else has done this in a more straighforward way. – flaria Jul 24 '20 at 17:09
  • It may worth to take a look on https://stackoverflow.com/a/60022121/1384539 – Thiago Custodio Jul 24 '20 at 17:40