Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to acquire token silently or via redirect using msal-browser

Tags:

adal

msal

msal.js

I'm trying to develop a VueJS single page application that logs you into AAD so that I can get an access token to call various APIs (e.g. Graph).

Once a user is logged in, you have to acquire a token and there are two ways of doing this: silently (and if this fails, using the redirect experience).

However, I am unable to acquire a token using both approaches:

export default class AuthService {
  constructor() {
    console.log('[AuthService.constructor] started constructor');

    this.app = new Msal.PublicClientApplication(msalConfig);
    this.signInType = 'loginRedirect';
  }

  init = async () => {
    console.log('[AuthService.init] started init');

    await this.app.handleRedirectPromise().catch(error => {
      console.log(error);
    });


    try {

      let signedInUser = this.app.getAllAccounts()[0];

      //  if no accounts, perform signin
      if (signedInUser === undefined) {
        alert(this.app.getAllAccounts().length == 0)
        await this.signIn();
        signedInUser = this.app.getAllAccounts()[0];
        console.log("user has been forced to sign in")
      }

      console.log("Signed in user is: ", signedInUser);

      // Acquire Graph token
      try {
        var graphToken = await this.app.acquireTokenSilent(authScopes.graphApi);
        console.log("(Silent) Graph token is ", graphToken);
        alert(graphToken);
      } catch (error) {
        alert("Error when using silent: " + error)
        try {
          var graphToken = await this.app.acquireTokenRedirect(authScopes.graphApi);
        } catch (error) {
          alert ("Error when using redirect is: " +  error)
        }

        alert("(Redirect) Graph token is " + graphToken);
      }

    } catch (error) {
      console.log('[AuthService.init] handleRedirectPromise error', error);
    }
  }

  signIn = async () => {
    console.log('[AuthService.signIn] signInType:', this.signInType);
    this.app.loginRedirect("user.read", "https://xxx.azurewebsites.net/user_impersonation");
  }

  signOut = () => {
    this.app.logout();
  }
}

Once I load the SPA, I get redirected to the AAD login page.

And then I get the following alert prompts:

Error when using silent: ClientAuthError: no_account_in_silent_request: Please pass an account object, silent flow is not supported without account information

Error when using redirect is: BrowserAuthError: interaction_in_progress: Interaction is currently in progress. Please ensure that this interaction has been completed before calling an interactive API.

(Redirect) Graph token is undefined

Even though I'm signed in, why does acquireTokenSilent think I'm not signed in?

And what does BrowserAuthError: interaction_in_progress mean? I searched this online and the only result I found was because someone was using an outdated version of msal-browser. In my case, I'm using the latest and greatest (v2.0.1).


Update 1:

I've fixed my silent token acquisition by using the following code excerpt:

const silentRequest = {
  account: signedInUser,
  scopes: authScopes.graphApi.scopes1
}
var graphToken = await this.app.acquireTokenSilent(silentRequest);

Looks like I was passing my scopes in the wrong format (i.e. supposed to pass an array and it wasn't actually an array!)

There is however a discrepancy in the Microsoft documentation for how to use acquireTokenSilent

Over here, we're told to just pass in a set of scopes to the acquireTokenSilent method. However, over here, we're told to also pass in the accountInfo alongside the scopes.

Let's see if I can get acquireTokenRedirect working now...


Update 2: After much trial and error, I finally got acquireTokenRedirect working.

import * as Msal from '@azure/msal-browser';

const msalConfig = {
  auth: {
    clientId:    "XYZ",
    authority:   "ABC",
  },
  cache: {
    cacheLocation: 'localStorage',
    storeAuthStateInCookie: true
  }
};

export default class AuthenticationService {

  constructor() {

    this.app = new Msal.PublicClientApplication(msalConfig)
  }

  init = async () => {

    try {
      let tokenResponse = await this.app.handleRedirectPromise();

      let accountObj;
      if (tokenResponse) {
        accountObj = tokenResponse.account;
      } else {
        accountObj = this.app.getAllAccounts()[0];
      }

      if (accountObj && tokenResponse) {
        console.log("[AuthService.init] Got valid accountObj and tokenResponse")
      } else if (accountObj) {
        console.log("[AuthService.init] User has logged in, but no tokens.");
        try {
          tokenResponse = await this.app.acquireTokenSilent({
            account: this.app.getAllAccounts()[0],
            scopes: ["user.read"]
          })
        } catch(err) {
          await this.app.acquireTokenRedirect({scopes: ["user.read"]});
        }
      } else {
        console.log("[AuthService.init] No accountObject or tokenResponse present. User must now login.");
        await this.app.loginRedirect({scopes: ["user.read"]})
      }
    } catch (error) {
      console.error("[AuthService.init] Failed to handleRedirectPromise()", error)
    }

  }

}
like image 657
chivano Avatar asked Aug 10 '20 20:08

chivano


People also ask

How do I get my Msal token?

In MSAL, you can get access tokens for the APIs your app needs to call using the acquireTokenSilent method which makes a silent request(without prompting the user with UI) to Azure AD to obtain an access token.

How do you get token from Msal react?

Acquiring an access tokenreact-aad-msal exposes a getAccessToken method you can use to obtain an access token before calling an API. import { MsalAuthProvider } from "react-aad-msal"; const authProvider = new MsalAuthProvider(config, authenticationParameters, options); const accessToken = authProvider.

What is Msal redirect?

Sign-out with a redirect MSAL. js provides a logout method in v1, and logoutRedirect method in v2 that clears the cache in browser storage and redirects the window to the Azure AD sign-out page.

How do you use acquireTokenSilent?

js is to first attempt a silent token request by using the acquireTokenSilent method. When this method is called, the library first checks the cache in browser storage to see if a valid token exists and returns it. When no valid token is in the cache, it attempts to use its refresh token to get the token.


1 Answers

Had to go through some trial and error but finally got acquireTokenRedirect working successfully, this is the excerpt which may help others:

import * as Msal from '@azure/msal-browser';

const msalConfig = {
  auth: {
    clientId:    "XYZ",
    authority:   "ABC",
  },
  cache: {
    cacheLocation: 'localStorage',
    storeAuthStateInCookie: true
  }
};

export default class AuthenticationService {

  constructor() {

    this.app = new Msal.PublicClientApplication(msalConfig)
  }

  init = async () => {

    try {
      let tokenResponse = await this.app.handleRedirectPromise();

      let accountObj;
      if (tokenResponse) {
        accountObj = tokenResponse.account;
      } else {
        accountObj = this.app.getAllAccounts()[0];
      }

      if (accountObj && tokenResponse) {
        console.log("[AuthService.init] Got valid accountObj and tokenResponse")
      } else if (accountObj) {
        console.log("[AuthService.init] User has logged in, but no tokens.");
        try {
          tokenResponse = await this.app.acquireTokenSilent({
            account: this.app.getAllAccounts()[0],
            scopes: ["user.read"]
          })
        } catch(err) {
          await this.app.acquireTokenRedirect({scopes: ["user.read"]});
        }
      } else {
        console.log("[AuthService.init] No accountObject or tokenResponse present. User must now login.");
        await this.app.loginRedirect({scopes: ["user.read"]})
      }
    } catch (error) {
      console.error("[AuthService.init] Failed to handleRedirectPromise()", error)
    }

  }

}
like image 98
chivano Avatar answered Sep 24 '22 23:09

chivano