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

12,361

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)
    }

  }

}
Share:
12,361

Related videos on Youtube

chivano
Author by

chivano

Updated on June 04, 2022

Comments

  • chivano
    chivano almost 2 years

    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)
        }
    
      }
    
    }
    
  • fede s.
    fede s. over 3 years
    Hi, welcome to SO :) Your answer is currently using links to screencaps. To make your answer better, maybe your could use block quotes with the appropriate information, and links to the proper documentation or source instead. See the part about links and context here: stackoverflow.com/help/how-to-answer