AzureAD / microsoft-authentication-library-for-js

Microsoft Authentication Library (MSAL) for JS
http://aka.ms/aadv2
MIT License
3.64k stars 2.65k forks source link

Login loop after refresh token has expired #3148

Closed asynccuriosity closed 3 years ago

asynccuriosity commented 3 years ago

Library

Framework

Description

After the refresh token has expired(the current lifetime is a day), when the application tries to call aquireTokenSilent, it fails with interactionRequiredAuthError. That error is caught by the application and acquireTokenRedirect is then called. The application is then taken to the login page where the user details are pre-populated via login_hint present in the URL, The auth code is obtained(i.e the code is present in the url) and when aquireTokenSilent is called, it again fails with interactionRequiredAuthError triggering acquireTokenRedirect which results in a login loop.

Once the user clears cookies and cache data, it logs in after that just fine.

The login mechanism works just fine when the refresh token has not expired.

The issue was noted in msal-browser 2.8.0, upgraded to 2.11.2, but still the same issue.

Error Message

InteractionRequiredAuthError: interaction_required

MSAL Configuration

this.msalInstance = new PublicClientApplication({
        auth: {
          clientId: environment.authClientId,
          redirectUri: location.origin,
          navigateToLoginRequestUrl: false
        },
        cache: {
          cacheLocation: 'localStorage'
        }
      });

Reproduction steps

async token () {
  const account = this.getActiveAccount() || this.getAllAccounts()[0];
        if (account) {
          const silentRequest = {
            account: account,
            scopes: [environment.scope]
          };
          try {
            const tokenResponse = await this.msalInstance.acquireTokenSilent(silentRequest);
            return tokenResponse.accessToken;
          } catch (error) {
            console.log("Error obtaining token ", error);
            if (error instanceof InteractionRequiredAuthError) {
              // fallback to interaction when silent call fails
              const redirectStartPage = window.location.href;
              this.msalInstance.acquireTokenRedirect({...silentRequest, redirectUri: redirectStartPage});
            }
          }
        } else {
          console.log("No Accounts present");
        }
  return '';
}

getAllAccounts(): AccountInfo[] {
  return this.msalInstance.getAllAccounts();
}

getActiveAccount(): AccountInfo {
  return this.msalInstance.getActiveAccount();
}

Expected behavior

After the refresh token has expired the user is able to log in just fine without a login loop.

Identity Provider

Browsers/Environment

Regression

Security

Source

pkanher617 commented 3 years ago

@asynccuriosity Are you calling handleRedirectPromise and allowing the promise to resolve before calling acquireTokenSilent again?

asynccuriosity commented 3 years ago

Hi @pkanher617 adding the following to the top of our auth-guard fixed the issue.

const authenticationResult = await this.authService.handleRedirectObservable().pipe(take(1)).toPromise();

There was an instance when the user was already logged in, the code would not wait for the handleRedirectObservable to complete and call for the token.

Thank you for your help. You can consider this issue resolved.