wlyles
wlyles

Reputation: 2316

Cannot verify AAD token obtained via adal-angular4

I've got a web app written in Angular 6 using the adal-angular4 package to perform authentication via AAD. I am trying to get the UI to communicate with a backend service using this token. However, I keep getting a 401 Unauthorized response when trying to call the server's APIs even when all of the validation should be turned off. Inspecting the token with jwt.io yields an "invalid signature" error at the bottom of the page, but otherwise the token is readable.

As for AAD configuration, both the frontend app and backend service are registered as separate AAD apps, so any references to clientId or clientAppId refer to the client's application id while apiServiceId refers to that of the backend service. I have also exposed a scope in the backend app registration, added an API permission to the frontend app registration for that scope, and authorized the frontend app in the backend app. I have also enabled implicit grant for both id tokens and access tokens for both services. Here's the relevant code:

app.module.ts:

// imports

@NgModule({
  declarations: [ ... ],
  imports: [ ... ],
  providers: [
    AdalService,
    AdalGuard,
    { provide: HTTP_INTERCEPTORS, useClass: AdalInterceptor, multi: true }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts:

import { Component } from '@angular/core';

import { AdalService } from 'adal-angular4';
import { environment } from '../environments/environment';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {
  title = 'Tour of Heroes';

  constructor(private adalService: AdalService) {
    adalService.init(environment.adalConfig);
  }
}

toolbar.component.ts (where the login button lives):

import { Component, OnInit } from '@angular/core';
import { AdalService } from 'adal-angular4';

@Component({
  selector: 'app-toolbar',
  templateUrl: './toolbar.component.html',
  styleUrls: ['./toolbar.component.css']
})
export class ToolbarComponent implements OnInit {

  constructor(private adalService: AdalService) { }

  ngOnInit() {

    this.adalService.handleWindowCallback();

    console.log(this.adalService.userInfo);
  }

  login() {
    this.adalService.login();
  }

  logout() {
    this.adalService.logOut();
  }

  get authenticated(): boolean {
    return this.adalService.userInfo.authenticated;
  }

  get username(): string {
    return this.adalService.userInfo.userName;
  }
}

environment.ts:

export const environment = {
  production: true,
  adalConfig: {
    tenant: 'MyTenant.onmicrosoft.com',
    clientId: '<clientId>',
    endpoints: {
      "https://localhost:8443/api": "<apiServiceId>"
    }
  }
};

From Startup.cs:

// TODO: Put strings in config
const string tenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
const string clientAppId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
appBuilder.UseWindowsAzureActiveDirectoryBearerAuthentication(
    new WindowsAzureActiveDirectoryBearerAuthenticationOptions
    {
        Tenant = "MyTenant.onmicrosoft.com",
        TokenValidationParameters = new TokenValidationParameters
        {
            // Authentication still fails when all bools below are set to false.
            NameClaimType = ClaimTypes.Name,
            AuthenticationType = "AADJwt",
            ValidateLifetime = true,
            RequireExpirationTime = true,
            ValidateAudience = true,
            ValidAudiences = new[]
            {
                clientAppId,
            },
            ValidateIssuer = true,
            ValidIssuers = new[]
            {
                $"https://sts.windows.net/{tenantId}/",
                $"https://login.microsoftonline.net/{tenantId}/",
            },
        }
    });

Controller.cs:

[Authorize]
public class MyController : ApiController
{
    // APIs
}

Here's what the token looks like according to jwt.io:

Header:

{
  "typ": "JWT",
  "alg": "RS256",
  "x5t": "nbCwW11w3XkB-xUaXwKRSLjMHGQ",
  "kid": "nbCwW11w3XkB-xUaXwKRSLjMHGQ"
}

Payload:

{
  "aud": "<clientId>",
  "iss": "https://sts.windows.net/<tenantId>/",
  "iat": 1547682045,
  "nbf": 1547682045,
  "exp": 1547685945,
  "aio": "AVQAq/8KAAAAONOm6T/DrjjHFUTe/uPcsFcv2Iye85/EtY+cFYyq+X69OcQlHyqqPcYF0cjRWHyIRnnkcr7PkSTHp5bRb40AGUhVS5yuG53RCO0lNAQCBfE=",
  "amr": [
    "pwd",
    "rsa"
  ],
  "email": "[email protected]",
  "family_name": "asdf",
  "given_name": "asdf",
  "idp": "https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/",
  "ipaddr": "x.x.x.x",
  "name": "asdf asdf",
  "nonce": "1a80bf29-e720-4abc-91c3-bc37c066286a",
  "oid": "f4f2107f-a760-4db4-b5d6-9a9fb6d2cb6b",
  "sub": "ll04ffs2YHBR0esHBJp9vevJEbgITRC9ushAQdZbB7U",
  "tid": "d93850bc-6bc9-4a51-9975-13e297ee0710",
  "unique_name": "[email protected]",
  "uti": "A7mEPuQ6REKQIFShTCoDAA",
  "ver": "1.0"
}

Why would my authentication be failing? Everything in the token that I want to verify seems to match what I'm expecting, so I have no idea why I keep getting a 401. The fact that jwt.io can't validate the signature makes me think something is wrong there; what could it be?

Edit: Added AAD setup info and provided an updated environment.ts, which includes the endpoints array. From what I'm reading, the AdalInterceptor will automatically acquire and inject access tokens based on endpoints. The token that I get is functionally identical to the one shown above. Is there some other configuration I'm missing?

Upvotes: 1

Views: 452

Answers (1)

wlyles
wlyles

Reputation: 2316

So it turns out I was setting up the middleware in the incorrect order in startup. See this answer, where the asker had to register AAD authentication earlier in the pipeline in order to get things working. Otherwise you may just get 401 responses every time regardless of configuration.

Upvotes: 1

Related Questions