question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Unable to acquire token silently after B2C login and redirection to SPA redirectUri

See original GitHub issue

Core Library

MSAL.js v2 (@azure/msal-browser)

Core Library Version

2.24.0

Wrapper Library

Not Applicable

Wrapper Library Version

None

Description

My understanding is that the B2C flow with MSAL.js for a sign-in only policy works in the following way (please correct if mistaken):

  1. create msalInstance with the msal config
  2. call msalInstance.handleRedirectPromise()
  3. when the handleRedirectPromise promise returns, call loginRedirect with the correct scopes, which will redirect the user to a b2c login page, and on completion, navigate back to the SPA redirectUri
  4. once the the b2c login is complete, and the user is navigated back to the SPA redirectUri, we use the msalInstance.acquireTokenSilent with the correct scopes and current active account passed in as function option params, so we can get all the user information such as the accessToken. This is how we translate the information the MSAL stores in the browser (sessionStorage in this case) to user information and an access token that I can use.

Steps 1-3 are working as expected. The issue is specifically that on step 4, after the redirect happens when I login, I am unable to acquire the token. There are no active accounts (when I attempt to retrieve with MSAL.js), and I am also unable to acquire the token with acquireTokenSilent. The error I am getting is the following: BrowserAuthError: no_account_error: No account object provided to acquireTokenSilent and no active account has been set. Please call setActiveAccount or provide an account on the request.

The issue is that I can’t call setActiveAccount when the active account does not exist (nothing exists for msalInstance.getAllAccounts().

I am using the following code for step 4 - which is run after the the successful login redirect to the redirectUri:

        const accounts = msalInstance.getAllAccounts();
        if (accounts?.length > 0) {
         msalInstance.setActiveAccount(accounts[0]);
        }

        if (msalInstance.getActiveAccount()) {
         const msalScopes = { scopes: ["my-example-b2c-web-api-scope"] }
         msalInstance
            .acquireTokenSilent(msalScopes)
            .then((tokenResponse) => {
              // Do something with the tokenResponse
              console.log('acquireTokenSilent res', tokenResponse);
            })
            .catch((error) => {
              console.log('acquireTokenSilent error', error);
              if (error instanceof msal.InteractionRequiredAuthError) {
                // fallback to interaction when silent call fails
                return msalInstance.acquireTokenRedirect(
                  msalScopes
                );
              }
            });

The issue is that because this.msalInstance.getAllAccounts() is null (empty), I cannot successfully retrieve the token information from the acquireTokenSilent call.

However, if I sign-in the first time, and get navigated back to the redirectUri, and then I run the sign-in again (the msalInstance.handleRedirectPromise() and then loginRedirect() code), I will successfully get the accounts and the token response from acquireTokenSilent. So I know that it most likely is not a configuration issue, or something to do with scopes or permissions.

The issue that needs to be figured out, is how can the token be retrieved on the successful navigation from the b2c login to the SPA redirectUri? Having to trigger a sign-in twice is not doable for the app use-case. I am hoping there is a better way to do this and to fix the issue of being able to retrieve the token on that initial redirect navigation to the redirectUri of the SPA.

The expected behavior is that when the SPA redirectUri is hit, that I will be able to get the access token and other user information. acquireTokenSilent doesn’t seem to be working as expected for this use case.

Error Message

BrowserAuthError: no_account_error: No account object provided to acquireTokenSilent and no active account has been set. Please call setActiveAccount or provide an account on the request.

Msal Logs

n/a

MSAL Configuration

        const msalConfig = {
          auth: {
            clientId: 'example123',
            authority: 'b2c-example',
            redirectUri: 'localhost:4200/redirect-uri',
            knownAuthorities: ['my-b2c-web-api-scope'],
            postLogoutRedirectUri: 'localhost:4200/redirect-uri',
            navigateToLoginRequestUrl: true,
          },
          cache: {
            cacheLocation: 'sessionStorage',
            storeAuthStateInCookie: true,
          },
          system: {
            loggerOptions: {
              loggerCallback: (level, message, containsPii) => {
                if (containsPii) {
                  return;
                }
                switch (level) {
                  case msal.LogLevel.Error:
                    console.error(message);
                    return;
                  case msal.LogLevel.Info:
                    console.info(message);
                    return;
                  case msal.LogLevel.Verbose:
                    console.debug(message);
                    return;
                  case msal.LogLevel.Warning:
                    console.warn(message);
                    return;
                }
              },
            },
          },
        };

Relevant Code Snippets

     // initial code 
    msalInstance
      .handleRedirectPromise()
      .then((tokenResponse) => {
        if (tokenResponse === null) {
          // If the tokenResponse === null, you are not coming back from an auth redirect.
          msalInstance.loginRedirect(this.msalApiScopes);
        } else {
          // If the tokenResponse !== null, then you are coming back from a successful authentication redirect.
          console.log('success res', tokenResponse);
        }
      })
      .catch((err) => {
        // handle error, either in the library or coming back from the server
        console.log('err', err);
      });

        // code that runs after the redirectUri returns
        const accounts = msalInstance.getAllAccounts();
        if (accounts?.length > 0) {
          msalInstance.setActiveAccount(accounts[0]);
        }

         const msalScopes = { scopes: ["my-example-b2c-web-api-scope"] }
         msalInstance
            .acquireTokenSilent(msalScopes)
            .then((tokenResponse) => {
              // Do something with the tokenResponse
              console.log('acquireTokenSilent res', tokenResponse);
            })
            .catch((error) => {
              console.log('acquireTokenSilent error', error);
              if (error instanceof msal.InteractionRequiredAuthError) {
                // fallback to interaction when silent call fails
                return msalInstance.acquireTokenRedirect(
                  msalScopes
                );
            });


Reproduction Steps

see initial description

Expected Behavior

Be able to get token through acquireTokenSilent method directly after the redirect back to the SPA redirectUri after the b2c login is completed. Not having to go through the handleRedirectPromise a second time

Identity Provider

Azure B2C Basic Policy

Browsers Affected (Select all that apply)

Chrome

Regression

No response

Source

External (Customer)

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:14 (13 by maintainers)

github_iconTop GitHub Comments

1reaction
bmahallcommented, Jun 27, 2022

@danielvoigt I am currently looking into this , thanks for your patience!

0reactions
msftbot[bot]commented, Sep 20, 2022

@danielvoigt This issue has been automatically marked as stale because it is marked as requiring author feedback but has not had any activity for 5 days. If your issue has been resolved please let us know by closing the issue. If your issue has not been resolved please leave a comment to keep this open. It will be closed automatically in 7 days if it remains stale.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Unable to acquire token silently or via redirect using msal ...
I've fixed my silent token acquisition by using the following code excerpt: const silentRequest = { account: signedInUser, scopes: authScopes.
Read more >
acquireTokenSilent() returns an access token signed by the ...
I have an SPA that uses MSAL (msal-browser@2.7.0) to authenticate against Azure AD B2C. I call loginRedirect() and everything works as ...
Read more >
Microsoft Azure AD B2C and refresh tokens for Single Page ...
First, tokens must be retrieved using a background POST request instead of a parameter in the redirect URI (i.e. Implicit flow). Second, refresh ......
Read more >
Microsoft Authentication Library for JavaScript (MSAL.js)
If you are using an interactive token call, it must match the login method used in your application. ( loginPopup => acquireTokenPopup ,...
Read more >
Sign users in to your SPA using the redirect model
Redirect URI : http://localhost:4200/login/callback; Post Logout Redirect ... That token is invalidated after the new one is generated and returned in the ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found