7
votes

I am authenticating my Single Page App (Angular4) with Azure AD, and using Adal.js for the same. On the login page, I click a button that redirects to Microsoft AAD and upon successful login it redirects back to application home page, and receives id_token and user info from JWT.

I need the access_token for back-end API access, which I am trying to acquire through the the ADAL AuthenticationContext's getCachedToken() method, and sending the clientId as parameter:

this.context.getCachedToken(this.configService.AdalConfig.clientId)

But this method returns the same token which is stored in session storage as id_token (adal.idtoken). It basically creates a new item in session storage by with a concatenated key, which has same value as id_token

adal.access_token.key + clientId = id_token

ex: adal.access_token.key239f6fc7-64d2-3t04-8gfd-501efc25adkd = <id-token-value>.

I also tried to fetch access_token with AuthenticationContext.acquireToken() method, but it too gave the id_token back.

Where am I going wrong?

EDIT: posting the code. I am calling the function login(), and after successful login, trying to get the access token in home page via get accessToken() property accessor in adal.config.ts.

config.service.ts

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

@Injectable()
export class ConfigService {
  constructor() {}
  public get AdalConfig(): any {
    return {
      tenant: 'common',
      clientId: <application-id>,
      redirectUri: window.location.origin + '/',
      postLogoutRedirectUri: window.location.origin + '/'
    };
  }
}

adal.service.ts

import { ConfigService } from './config.service';
import { Injectable } from '@angular/core';
import { adal } from 'adal-angular';
let createAuthContextFn: adal.AuthenticationContextStatic = AuthenticationContext;

@Injectable()
export class AdalService {
  private context: adal.AuthenticationContext;
  constructor(private configService: ConfigService) {
    this.context = new createAuthContextFn(configService.AdalConfig);
  }

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

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

  handleCallback() {
    this.context.handleWindowCallback();
  }

  public get userInfo() {
    return this.context.getCachedUser();
  }

  public get accessToken() {
    return this.context.getCachedToken(this.configService.AdalConfig.clientId);
    // return this.context.acquireToken(this.configService.AdalConfig.clientId, function(message, token, response) {
    //   console.log(message, token, response);
    // });
  }

  public get isAuthenticated() {
    return this.userInfo && this.accessToken;
  }
}
2
you should post your entire authentication code...Shawn Tabrizi
For authentication I am just creating an AdalConfig object with all the required info such as tenant, clientId, redirectUri etc, and then initializing a new AuthenticationContext using the AdalConfig, and then using the initialized context's methods. I have posted the method call. Please let me know what else is required.Rishabh
Where are you specifying the resource you want to call? You need to post your code or no one is going to be able to help you.Shawn Tabrizi
Posted the config.service.ts and adal.service.ts code, that's where I am specifying all the required info by AAD. Hope this will help :)Rishabh
I think Shawn is on the right track :) I had a similar problem with ADAL.JS when I somehow specified the resource wrong so it got a token for itself (id token).juunas

2 Answers

4
votes

Actually, after a bit of reading, turned out that connecting SPA's to Azure AD requires OAuth 2.0 Implicit Grant flow. The Microsoft documentation says:

In this scenario, when the user signs in, the JavaScript front end uses Active Directory Authentication Library for JavaScript (ADAL.JS) and the implicit authorization grant to obtain an ID token (id_token) from Azure AD. The token is cached and the client attaches it to the request as the bearer token when making calls to its Web API back end, which is secured using the OWIN middleware.

So, it's the id_token itself that I need to send to the back-end APIs, which in turn can be validated and used. More info about validation is given here:

Just receiving an id_token is not sufficient to authenticate the user; you must validate the id_token's signature and verify the claims in the token per your app's requirements. The v2.0 endpoint uses JSON Web Tokens (JWTs) and public key cryptography to sign tokens and verify that they are valid.

You can choose to validate the id_token in client code, but a common practice is to send the id_token to a backend server and perform the validation there. Once you've validated the signature of the id_token, there are a few claims you will be required to verify.

0
votes

I've faced an issue like yours when I was trying to send the token to a .Net Core API endpoint. It worked for me when I sent the token from the adal.access.token.key node, on sessionStorage.

Using adal.access.token.key or adal.idtoken token values (they are the same) didn't work for me.

Valid token on adal.access.token.key node.