3
votes

Wondering if any Azure Active Directory/Angular 2/Adal experts can assist with this.

My set up is as follows: Azure Active Directory with three app registrations. One for the Web API, one for a WPF application and one for an Angular 2 Application.

The configuration for the Web API looks fairly standard:

<add key="ida:Tenant" value="<tenant>" />
<add key="ida:Audience" value="<ResourceURI>" />

The configuration for the WPF application (which is working) is

<add key="ida:AADInstance" value="https://login.windows.net/{0}"/>
<add key="ida:Tenant" value="<tenant>" />
<add key="ida:RedirectUri" value="<some redirect>" />
<add key="ida:ClientId" value="<client id as configured in Azure>" />
<add key="ApiResourceId" value="<The WEB API resource URI>"/>
<add key="ApiBaseAddress" value="<The base address of the Web API>"/>

Here is the Angular 2 configuration with NG2-ADAL and ADAL.js

   public get adalConfig(): any {


    return {
      instance: 'https://login.windows.net/',
      tenant: '<tenant>',
      clientId: '<client id configured for Angular App in Azure AD>',
      redirectUri: '<Angular 2 app Url>',
      extraQueryParameter: 'nux=1',
      postLogoutRedirectUri: '<Angular 2 app Url>',
      cacheLocation: 'localStorage',
      loginResource: '<Web API Resource Uri>',


    };

Both angular and WPF are able to login in AAD and get a token.

The problem lies with the Angular 2 Application.

The WPF application (which works) can login to Azure Active Directory correctly and the JWT (token) is accepted by the Web API application and the rest service can be called successfully.

When the WPF application logs in via microsoft.online.com/xxxxxxx it supplies a token which includes a claim like this:

 Header
{
  typ: "JWT",
  alg: "RS256",
  x5t: "_UgqXG_tMLduSJ1T8caHxU7cOtc",
  kid: "_UgqXG_tMLduSJ1T8caHxU7cOtc"
}
 Payload
{
  aud: "<web api resource uri>",
  iss: "https://sts.windows.net/1afea43f-3f0b-4a86-ad29-d444b80d2c91/",
  iat: 1488889876,
  nbf: 1488889876,
  exp: 1488893776,
  acr: "1",
  aio: "NA",
  amr: [
    "pwd"
  ]
}

And for this one - the aud parameter is the Web API resource URI. and it works perfectly

I am using ng2-adal which is a wrapper around adal.js for Angular 2. So when the Angular 2 Application Logs in it receives a token which looks like this:

 Header
{
  typ: "JWT",
  alg: "RS256",
  x5t: "_UgqXG_tMLduSJ1T8caHxU7cOtc",
  kid: "_UgqXG_tMLduSJ1T8caHxU7cOtc"
}
 Payload
{
  aud: "<angular app client id>",
  iss: "https://sts.windows.net/1afea43f-3f0b-4a86-ad29-d444b80d2c91/",
  iat: 1488888955,
  nbf: 1488888955,
  exp: 1488892855,
  amr: [
    "pwd"
  ]

}

So the main difference - and I think why I am getting the error is that the aud parameter in the token for the WPF application contains the correct audience and is being accepted, whereas the Angular Application is returning the Client ID from its token which does not match the Resource URI from the Web Api and hence is giving me a 401 error - Authorization has been denied for this request

Does anyone know how to configure the NG2-ADAL (adal.js) to get Azure active directory to issue a token which contains the Resource URI in the aud parameter of the JWT?

I have tried the loginResource parameter and the endpoints collection but with no luck.

Many Thanks...

3
Unsure why I'm voted down. I've tried to state clearly what I've tried, what has worked, what I think the problem is and a potential resolution.The Unculled Badger

3 Answers

3
votes

Ok, so I have managed to solve this. Basically the solution is the following:

The Angular 2 application must use its configured client id to do the primary authentication with AAD.

So its config is

tenant: '<tenant>',
clientId: '<configured angular 2 client id', 
redirectUri: 'some redirect',
postLogoutRedirectUri: 'some redirect',
cacheLocation: 'localStorage'

And the reason this wasn't working is that once the Angular Client has logged in successfully, it was trying to use this token to access a web api which (and this is the important bit) was on a different machine.

To solve this I called the function acquireToken on the ng2-adal like this:

this.adalService.acquireToken(<client id of target web api>).subscribe(p=>{
       console.log("Acquired token = " + p);

       let headers = new Headers();
        headers.append("Authorization", "Bearer " + p);
        someService.testWebApi(headers).subscribe(res => {
        console.log(res);
      }, (error) => {
        console.log(error);
      });


    }, (error => {
      console.log(error);
    }));

and then the service call worked.. So basically my scenario was that I was trying to do a CORS call to another web api on another machine and hence the token I got to login was not being allowed for making a web api call on another machine.

I have read that a more efficient way of doing this is to store it in the endpoints collection of the config so that when the uri is called for your remote machine, the correct resource endpoint is picked up, but I was unable to get this to work. I hope this helps someone.

1
votes

Actually, the value of aud in payload should be the Azure AD application's client ID, which your ng2 application acquire access token from. So, if your Web API application on Azure is protected by this AzureAD application, the access token get from ng2 should be able to authorize the request calls against to your Web API.

I have a quick test leverage the NG2 ADAL sample, use the simplest configuration as following:

{
  tenant: '<tenant id>',
  clientId: '<azure ad application client id>', // also use this application to protect the backed services
  redirectUri: window.location.origin + '/',
  postLogoutRedirectUri: window.location.origin + '/'
}

It works fine on my side.

Please double check your configurations and try again.

1
votes

I had the same problem. In endpoints I had put APP URI ID, replaced it with App ID.

var endpoints = {

    // Map the location of a request to an API to a the identifier of the associated resource
    "https://localhost:44327/": "<Application ID>" // Not APP URI ID
 };

Also enabled CORS for Web API. Followed this: https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api