4
votes

I've been following this example to access azure active directory from an angular (4) application: https://github.com/benbaran/adal-angular4-example

I can authenticate to AD but then I want to make subsequent calls to an API / APP also registered in AD.

I have tried using this example:

public CallAPI() {
  let headers = new Headers({ 'Content-Type': 'application/json' });
  var token;

  this.service.acquireToken("{client id}").subscribe(p => {
    token = p;

    headers.append("Authorization", 'Bearer ' + token);

    let options = new RequestOptions({ headers: headers });
    // Make the HTTP request:
    this.httpClient.get('https://localhost:45678/stuff', options).subscribe(data => {
      // Read the result field from the JSON response.
      this.results = data['results'];
      console.log(data);
    });

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

The first issue I have is a CORS error. I am running the client app on localhost:and get:

XMLHttpRequest cannot load https://localhost:45678/stuff. Redirect from 'https://localhost:45678/stuff' to 'https://login.microsoftonline.com/..........' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://localhost:4200' is therefore not allowed access.

The app / API I am trying to access is also running locally (both client and server are https)

They are both registered applications in active directory and their signon/app id uris are set to their respective localhost addresses.

The app / api uses service stack and is setup thus:

app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
  new WindowsAzureActiveDirectoryBearerAuthenticationOptions
  {
    TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
    {
      ValidAudience = Audience
    },
    Tenant = Domain
  });

public override void Configure(Container container)
{
  AddPreRequestFilters();
  AddErrorResponseFilters();

  this.Plugins.AddRange(ServiceConfiguration.GetPlugins());
  this.Plugins.Add(new SwaggerFeature());
  this.Plugins.Add(new CorsFeature(allowedHeaders: "Origin, X-Requested-With, Content-Type, Accept, Authorization", allowCredentials: true));
  ServiceConfiguration.Configure(container);

}

To bypass the CORS error I used the Allow-Control-Allow-Origin chrome extension, using this I get an OPTIONS request then a 302 (to my 'stuff' endpoint) which contains my authorization: Bearer {token} header. Finally there is an OPTIONS and GET (with auth header) to login.microsoft.com/..../oath2...

This always fails to sign in.

My ADAL config looks like this:

const config: adal.Config = {                          
  tenant: 'xxxx.onmicrosoft.com',            
  clientId: 'xxxxxxxxxxxxxx',   // client id of AD app
  redirectUri: 'https://localhost:4200/', // the angular app
  cacheLocation: 'localStorage'

}

Is there anything obvious I am missing? I have also tried bypassing the acquireToken step using the endpoints property to no avail:

endpoints: {
  'https://localhost:45678/': 'https://localhost:45678/'   <-- the address of the API/APP I want to call
}
1
The problem is not with your site but instead the fact that the https://login.microsoftonline.com/ your code is redirecting the request to doesn’t send the Access-Control-Allow-Origin response header that’s needed for browsers to allow your frontend JavaScript code to access the response cross-origin. The only likely solution in this case is to not handle that login request from your frontend code but instead handle it from your backend code. The flow you’re trying to have it follow now is not going to work.sideshowbarker
Since you mentioned "They are both registered applications in active directory and their signon/app id uris are set to their respective localhost addresses" . You need to acquire token for your api app , what is the client id value of acquireToken function ? And what is the ValidAudience value in api app token validation logic ?Nan Yu
@NanYu The client id in the acquireToken is the application ID of the API service application registered in AD (in my example it's the 'stuff' endpoint / localhost:45678 ) The angular app logs in with AD and then gets a token back from acquireToken. The problem is when I try to use this token to access the API itself, I can see the authorization: Bearer..... is present but I always get a "we can't sign you in" html page response. My ValidAudience is set to the localhost address of the API i.e localhost:45678 ThanksMichael Harper

1 Answers

1
votes

We (well our senior developer) found a couple of issues when trying to access a registered API after logging in to AD (adal4.service.js)

First off in handleWindowCallback the requestType was always set to UNKNOWN in our case so the statematch was always false.

Bit of a hack here:

if(requestInfo.requestType === 'UNKNOWN') {
requestInfo.requestType = this.adalContext.REQUEST_TYPE.RENEW_TOKEN;
requestInfo.stateMatch = true;
}

We also had to change this:

else if (requestInfo.requestType === 
this.adalContext.REQUEST_TYPE.RENEW_TOKEN) {
this.adalContext.callback = 
window.parent.callBackMappedToRenewStates[requestInfo.stateResponse];
}

to this:

else if (requestInfo.requestType === 
this.adalContext.REQUEST_TYPE.RENEW_TOKEN) {
this.adalContext.callback =
window.parent.callBackMappedToRenewStates[
decodeURIComponent(requestInfo.stateResponse)];
}

The url in stateResponse was encoded (percentage symbols etc) so would never match leaving the callback null.

Hopefully this helps someone - maybe to find an even better solution!

Here's the fork: adal-angular4