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

Redirect loop after "invalid_grant" during token request #3171

Closed LexLuengas closed 3 years ago

LexLuengas commented 3 years ago

Library

Framework

Description

When a token request fails due to an "invalid_grant" error, the Angular app enters an infinite redirect loop and does not recover.

As a workaround, deleting the site's browser data allows for a successful token request, until the next "invalid_grant" error happens.

Error Message

After initialization, the app will try to make its first service call and request a token. This will fail and cause a redirect to "/", after which it will retry, failing again and entering a redirection loop. Below is the console log which includes MSAL logs. The part between "[Loop start]" and "[Loop end]" repeats indefinitely.

Navigated to http://localhost:4200/page/overview
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:43 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:handleRedirectStart
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:43 GMT] :  : @azure/msal-browser@2.12.0 : Info - handleRedirectPromise called but there is no interaction in progress, returning null.
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:43 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:handleRedirectEnd
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:43 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:handleRedirectStart
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:43 GMT] :  : @azure/msal-browser@2.12.0 : Info - handleRedirectPromise called but there is no interaction in progress, returning null.
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:43 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:handleRedirectEnd
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:43 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:handleRedirectStart
core.js:27701 Angular is running in development mode. Call enableProdMode() to enable production mode.
client:52 [WDS] Live Reloading enabled.

[Loop start]

[API request happens here, which will trigger a token request]
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:50 GMT] :  : @azure/msal-angular@2.0.0-beta.0 : Info - Interceptor - 1 scopes found for endpoint
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:50 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:acquireTokenStart
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:50 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:acquireTokenFromNetworkStart
zone-evergreen.js:1068 POST https://login.microsoftonline.com/a01b3b4a-1448-4fe9-b967-00927abe0460/oauth2/v2.0/token 400 (Bad Request)
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:51 GMT] :  : @azure/msal-browser@2.12.0 : Info - BrowserCacheManager.cleanRequestByState: Removing temporary cache items for state: ...50In19
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:51 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:acquireTokenFailure
msal-configuration.ts:43 [Tue, 09 Mar 2021 16:40:51 GMT] :  : @azure/msal-angular@2.0.0-beta.0 : Error - Interceptor - acquireTokenSilent rejected with error. Invoking interaction to resolve.
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:51 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:acquireTokenStart
Navigated to http://localhost:4200/
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:53 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:handleRedirectStart
client:52 [WDS] Live Reloading enabled.
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:53 GMT] :  : @azure/msal-common@4.0.2 : Info - in acquireToken call
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:53 GMT] :  : @azure/msal-browser@2.12.0 : Info - BrowserCacheManager.cleanRequestByState: Removing temporary cache items for state: ...3QifX0=
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:53 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:acquireTokenSuccess
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:53 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:handleRedirectEnd
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:53 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:handleRedirectStart
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:53 GMT] :  : @azure/msal-browser@2.12.0 : Info - handleRedirectPromise called but there is no interaction in progress, returning null.
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:53 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:handleRedirectEnd
msal-configuration.ts:46 [Tue, 09 Mar 2021 16:40:53 GMT] :  : @azure/msal-browser@2.12.0 : Info - Emitting event: msal:handleRedirectStart
core.js:27701 Angular is running in development mode. Call enableProdMode() to enable production mode.

[redirect to start page happens]
[Loop end]

The token request gives this 400 response:

{"error":"invalid_grant","error_description":"AADSTS700081: The refresh token has expired due to maximum lifetime. The token was issued on 2021-03-08T06:54:25.7893649+00:00 and the maximum allowed lifetime for this application is 1.00:00:00.\r\nTrace ID: a8447061-52d5-420b-b981-16a21be78600\r\nCorrelation ID: 422b3da2-7bcd-4edf-a46e-94149e7ff36c\r\nTimestamp: 2021-03-09 16:53:09Z","error_codes":[700081],"timestamp":"2021-03-09 16:53:09Z","trace_id":"a8447061-52d5-420b-b981-16a21be78600","correlation_id":"422b3da2-7bcd-4edf-a46e-94149e7ff36c","error_uri":"https://login.microsoftonline.com/error?code=700081","suberror":"bad_token"}

MSAL Configuration

{
    auth: {
      clientId: ...,
      authority: ...,
      navigateToLoginRequestUrl: false,
      redirectUri: '/',
      postLogoutRedirectUri: '/',
    },
    cache: {
      cacheLocation: BrowserCacheLocation.LocalStorage,
      storeAuthStateInCookie: false,
    },
}

Reproduction steps

Expected behavior

The MSAL interceptor will eventually acquire a valid token.

Identity Provider

Browsers/Environment

Regression

Security

Source

jo-arroyo commented 3 years ago

@LexLuengas Are you able to provide the code you are using in your app.component.ts? Also, are you using the MsalRedirectComponent?

LexLuengas commented 3 years ago

@jo-arroyo I am not using the MsalRedirectComponent.

app.component.ts

import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router';
import { MsalBroadcastService, MsalGuardConfiguration, MsalService } from '@azure/msal-angular';
import { debounceTime, filter, takeUntil, tap } from 'rxjs/operators';
import { EventMessage, EventType } from '@azure/msal-browser';
import { Subject } from 'rxjs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit, OnDestroy {
  appIsReady = false;
  isIframe = false;
  loggedIn = false;
  private readonly isDestroyed$ = new Subject<void>();

  constructor(
    private authService: MsalService,
    private msalBroadcastService: MsalBroadcastService,
  ) { }

  ngOnInit() {
    this.isIframe = window !== window.parent && !window.opener;
    this.appIsReady = true;

    this.checkAccount();

    this.authService.handleRedirectObservable().subscribe({
      next: (result) => {
        debugger;
      },
      error: (error) => {
        debugger;
      },
    });

    this.msalBroadcastService.msalSubject$
      .pipe(
        filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS || msg.eventType === EventType.ACQUIRE_TOKEN_SUCCESS || msg.eventType === EventType.SSO_SILENT_SUCCESS || msg.eventType === EventType.HANDLE_REDIRECT_END),
        takeUntil(this.isDestroyed$),
      )
      .subscribe((result) => {
        this.checkAccount();
      });
  }

  checkAccount() {
    this.loggedIn = this.authService.instance.getAllAccounts().length > 0;
  }

  ngOnDestroy(): void {
    this.isDestroyed$.next(undefined);
    this.isDestroyed$.complete();
  }
}

app.component.html

<div class="app">
  <ng-container *ngIf="appIsReady && loggedIn; else loadingSpinner">
    <div class="app__body">
      <router-outlet *ngIf="!isIframe"></router-outlet>
    </div>
  </ng-container>

  <router-outlet name="error-page" *ngIf="!isIframe"></router-outlet>

  <!-- Page-covering loading spinner shown when app is initializing -->
  <ng-template #loadingSpinner>
    <loading-spinner
      size="large"
      text="Initializing application..."
    ></loading-spinner>
  </ng-template>
</div>
jo-arroyo commented 3 years ago

@LexLuengas We do not recommend calling handleRedirectObservable() in the app.component.ts, as this does seem to cause looping. Please see our redirect doc for our guidance for redirects, both with and without the MsalRedirectComponent.

LexLuengas commented 3 years ago

Thanks. In the meantime I have cleared my app‘s localStorage, so I cannot reproduce the issue anymore. I have preemptively switched to the MsalRedirectComponent pattern as you pointed out. I will report back or create a new issue if the redirection problem reappears.

tnorling commented 3 years ago

Closing as this appears to have been solved. Please open a new issue if you continue to experience issues, thanks!