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

AcquireTokenSilent not working as expeced #7196

Closed renu10-dot closed 1 month ago

renu10-dot commented 3 months ago

Core Library

MSAL.js (@azure/msal-browser)

Core Library Version

2.22.0

Wrapper Library

MSAL Angular (@azure/msal-angular)

Wrapper Library Version

2.1.1

Public or Confidential Client?

Public

Description

We were using msal 1.1.2 version initially in my application but as we migrated to angular14 we migrated msal v1 to msal v2. I can see previously msal was saving msal.idtoken, authority in session storage but in v2 it is not saved msal.idtoken in session storage. So I explicitly saving token in session so I can read it in interceptor and send in header for http calls. We are calling acquireTokenSilent() after 50 mins ,10 mins before token gets expired. I am getting error "InteractionRequiredAuthError: invalid_grant: AADSTS65001: The user or administrator has not consented to use the application with ID " When we were using msal v1 idtoken was getting saved in session as per screenshot2.

Error Message

image MsalSession

MSAL Logs

No response

Network Trace (Preferrably Fiddler)

MSAL Configuration

const isIE = window.navigator.userAgent.indexOf("MSIE ") > -1 || window.navigator.userAgent.indexOf("Trident/") > -1; // Remove this line to use Angular Universal

export function loggerCallback(logLevel: LogLevel, message: string) {
  console.log(message);
}

export function MSALInstanceFactory(): IPublicClientApplication {
  return new PublicClientApplication({
    auth: {
      clientId:environment.clientId, //'bdecb075-4d5d-4de4-8006-7e5a23c5568d',
      redirectUri: environment.clientUrlDomain,//'http://localhost:4200',
      authority: environment.authority,
      navigateToLoginRequestUrl: false,//'https://login.microsoftonline.com/4cc65fd6-9c76-4871-a542-eb12a5a7800c'
      postLogoutRedirectUri:environment.clientUrlDomain
    },
    cache: {
      cacheLocation: BrowserCacheLocation.SessionStorage,
      storeAuthStateInCookie: isIE, // set to true for IE 11. Remove this line to use Angular Universal
    },
    system: {
      loggerOptions: {
        loggerCallback,
        logLevel: LogLevel.Info,
        piiLoggingEnabled: false
      }
    }
  });
}

export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
  const protectedResourceMap = new Map<string, Array<string>>();
  protectedResourceMap.set('https://graph.microsoft.com/v1.0/me', ['user.read'],);
  protectedResourceMap.set('/client/api/user/*', [ "scope-for-this-api"]);
  // protectedResourceMap.set('https://graph.microsoft-ppe.com/v1.0/me', ['user.read']); // PPE testing environment

  return {
    interactionType: InteractionType.Redirect,
    protectedResourceMap
  };
}

export function MSALGuardConfigFactory(): MsalGuardConfiguration {
  return {
    interactionType: InteractionType.Redirect,
    authRequest: {
      scopes: ['user.read',"openid","profile"]
    },
  };
}

Relevant Code Snippets

app.module.ts 
Providers added
 providers:  [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true
    },
    {
      provide: MSAL_INSTANCE,
      useFactory: MSALInstanceFactory
    },
    {
      provide: MSAL_GUARD_CONFIG,
      useFactory: MSALGuardConfigFactory
    },
    {
      provide: MSAL_INTERCEPTOR_CONFIG,
      useFactory: MSALInterceptorConfigFactory
    },
    MsalService,
    MsalGuard,
    MsalBroadcastService
  ],
  bootstrap: [AppComponent,MsalRedirectComponent]

app.component.ts

 constructor(
    @Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration,
    private authService: MsalService,
    private msalBroadcastService: MsalBroadcastService, private storageService: StorageService,
    private authenticationService:AuthenticationService
  ) {}

  ngOnInit(): void {
    this.isIframe = window !== window.parent && !window.opener; // Remove this line to use Angular Universal
    this.setLoginDisplay();

    this.authService.instance.enableAccountStorageEvents(); // Optional - This will enable ACCOUNT_ADDED and ACCOUNT_REMOVED events emitted when a user logs in or out of another tab or window
    this.msalBroadcastService.msalSubject$
      .pipe(
        filter((msg: EventMessage) => msg.eventType === EventType.ACCOUNT_ADDED || msg.eventType === EventType.ACCOUNT_REMOVED),
      )
      .subscribe((result: EventMessage) => {
        console.log("Result:-----",result);
        if (this.authService.instance.getAllAccounts().length === 0) {
          window.location.pathname = "/";
        } else {
          this.setLoginDisplay();
        }
      });
      this.msalBroadcastService.msalSubject$
   .pipe(
      filter(
         (msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS 
         || msg.eventType === EventType.SSO_SILENT_SUCCESS)
       ).subscribe((result: EventMessage) => {
          const payload = result.payload as AuthenticationResult;
            sessionStorage.setItem('exp', payload.expiresOn.getTime().toString());
            setTimeout(() => {
                this.setLoginDisplay();
                this.authenticationService.startTimer(this.authenticationService.getTokenTimeout())
            }, 500)
          console.log("Payload:-",payload);
    });

    this.msalBroadcastService.inProgress$
      .pipe(
        filter((status: InteractionStatus) => status === InteractionStatus.None),
        takeUntil(this._destroying$)
      )
      .subscribe(() => {
        this.checkAndSetActiveAccount();
        this.setLoginDisplay();
        if(!this.loginDisplay)
          {
            this.authenticationService.logIn();
          }

      })
  }

  setLoginDisplay() {
    this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
    if(this.loginDisplay)
      {
    const userName = this.authService.instance.getAllAccounts()[0].name;
        const userEmail = this.authService.instance.getAllAccounts()[0].username;
      const exp = this.authService.instance.getAllAccounts()[0].idTokenClaims.exp;
    console.log("Account:-",this.authService.instance.getAllAccounts()[0]);
        console.log("User Name:-",userName);
        console.log("User Email:-",userEmail);
        console.log("exp:-",exp);
    console.log("Token:-",this.authService.instance.getAllAccounts()[0].idToken);

        this.storageService.userName = userName
        this.storageService.userEmail = userEmail;
        this.storageService.tokenExp = +exp;
    this.storageService.msalToken = this.authService.instance.getAllAccounts()[0].idToken;
    this.authenticationService.loggedInSrc.next(true);
      }
  }

  checkAndSetActiveAccount(){
    /**
     * If no active account set but there are accounts signed in, sets first account to active account
     * To use active account set here, subscribe to inProgress$ first in your component
     * Note: Basic usage demonstrated. Your app may require more complicated account selection logic
     */
    let activeAccount = this.authService.instance.getActiveAccount();
    if (!activeAccount && this.authService.instance.getAllAccounts().length > 0) {
      let accounts = this.authService.instance.getAllAccounts();
      this.authService.instance.setActiveAccount(accounts[0]);
    }
  }

  ngOnDestroy(): void {
    this._destroying$.next(undefined);
    this._destroying$.complete();
  }
authentication.service.ts

public logIn() {
        console.log("Trying MS login");
        if(this.msalGuardConfig.authRequest)
            {
              this.msalService.loginRedirect({...this.msalGuardConfig.authRequest} as RedirectRequest)
            }
            else
            {
              this.msalService.loginRedirect();
            }
    }

public LoginSilent() {
        console.log("Login Silent called");
        this.removeOldToken();
        const scope = {
            scopes: [
                enviornment.clientid

            ],
        }
        this.msalService.instance.acquireTokenSilent(scope);

Reproduction Steps

with the help of code provided above you can reproduce the issue.

Expected Behavior

on AcquireTokenSilent token should be refreshed silently without redirecting user to login .

Identity Provider

Entra ID (formerly Azure AD) / MSA

Browsers Affected (Select all that apply)

Edge

Regression

No response

Source

Internal (Microsoft)

tnorling commented 3 months ago

Does it work after redirecting to AAD and providing consent?

Generally speaking, when you receive an interaction_required error it means the server requires some more information from the user to issue the tokens. To resolve you either need to let the user provide the required information (by redirecting) or change what you're asking for (reduced permissions the user has already granted consent for)

As an aside - v1 and v2 use different underlying auth flows, it's not going to be a 1:1 upgrade

renu10-dot commented 2 months ago

For the first time when login() is called it is redirected to AAD and gets the token back. We are calling LoginSilent() after 50mins(before 10 mins of token expire) so I can see its causing InteractionError as per above screenshot. @tnorling I have one more doubt, as I said in msal v1 its was saved msal.idtoken in sessionstorage by msal lib and in v2 I did not find that . I created variable msalToken and saved token returned by AAD in this variable in sessionStorage. does it causing issue when we are calling acquireTokenSilent and it is not able to read cached token? in V2 is there any way we can log msal.idtoken by msal package same as v1? so we can read from sessionStorage.getItem() in interceptor and send for api calls. Is there any api permissions required when we call acquireTokenSilent for application. When I googled the issue and tried to debug it I came across all above possibilities. Could you plz guide in this.

renu10-dot commented 2 months ago

main.js:1 [Mon, 22 Jul 2024 12:00:10 GMT] : [4dcb0723-96ad-4e3e-a862-47c9b3ffb15c] : @azure/msal-browser@2.39.0 : Info - PerformanceClient: No correlation id provided for initializeClientApplication, generating main.js:1 [Mon, 22 Jul 2024 12:00:10 GMT] : @azure/msal-browser@2.39.0 : Info - Emitting event: msal:initializeStart main.js:1 [Mon, 22 Jul 2024 12:00:10 GMT] : @azure/msal-browser@2.39.0 : Info - Emitting event: msal:initializeEnd main.js:1 [Mon, 22 Jul 2024 12:00:10 GMT] : @azure/msal-browser@2.39.0 : Info - Emitting event: msal:handleRedirectStart

main.js:1 [Mon, 22 Jul 2024 12:00:10 GMT] : [0af849cd-6931-4eea-87a3-d34dd8907476] : @azure/msal-common@13.3.3 : Info - in acquireToken call in auth-code client main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-browser@2.39.0 : Info - BrowserCacheManager: addTokenKey - idToken added to map main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-browser@2.39.0 : Info - BrowserCacheManager: addTokenKey - accessToken added to map main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-browser@2.39.0 : Info - BrowserCacheManager: addTokenKey - refreshToken added to map main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-browser@2.39.0 : Info - Emitting event: msal:loginSuccess main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-browser@2.39.0 : Info - Emitting event: msal:handleRedirectEnd main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 Account:- ObjectauthorityType: "MSSTS"environment: "login.windows.net"homeAccountId: "cd24c2b5-c829-400e-84ec-6d428e4e3acb.4cc65fd6-9c76-4871-main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token

main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token

main.js:1 [Mon, 22 Jul 2024 12:00:12 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:12 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:12 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:12 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:12 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token

main.js:1 [Mon, 22 Jul 2024 12:00:12 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:12 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token

main.js:1 Login Silent called

main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : @azure/msal-browser@2.39.0 : Info - Emitting event: msal:acquireTokenStart main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getAccessToken - No token found main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getRefreshToken - returning refresh token main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : [29c332b8-63b8-4e49-882e-46ef9029150d] : @azure/msal-common@13.3.3 : Info - SilentFlowClient:acquireCachedToken - No access token found in cache for the given properties. main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : @azure/msal-browser@2.39.0 : Info - Emitting event: msal:acquireTokenFromNetworkStart main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : @azure/msal-common@13.3.3 : Info - CacheManager:getRefreshToken - returning refresh token login.microsoftonline.com/4cc65fd6-9c76-4871-a542-eb12a5a7800c/oauth2/v2.0/token:1

    Failed to load resource: net::ERR_NAME_NOT_RESOLVED

main.js:1 [Mon, 22 Jul 2024 14:07:06 GMT] : @azure/msal-browser@2.39.0 : Info - Emitting event: msal:acquireTokenFailure main.js:1 TokenResponse:- Bzone_symbolstate: truezone_symbolvalue: undefinedSymbol(Symbol.species): (...)Symbol(Symbol.toStringTag): (...)

I can see from logs its not getting accessToken from cache @tnorling can you plz guide what I am doing wrong . I have shared all code .

tnorling commented 2 months ago

For the first time when login() is called it is redirected to AAD and gets the token back. We are calling LoginSilent() after 50mins(before 10 mins of token expire) so I can see its causing InteractionError as per above screenshot. @tnorling I have one more doubt, as I said in msal v1 its was saved msal.idtoken in sessionstorage by msal lib and in v2 I did not find that . I created variable msalToken and saved token returned by AAD in this variable in sessionStorage. does it causing issue when we are calling acquireTokenSilent and it is not able to read cached token? in V2 is there any way we can log msal.idtoken by msal package same as v1? so we can read from sessionStorage.getItem() in interceptor and send for api calls. Is there any api permissions required when we call acquireTokenSilent for application. When I googled the issue and tried to debug it I came across all above possibilities. Could you plz guide in this.

You should not be storing or looking up tokens in local/session storage. Caching is an implementation detail of the library and keys/values are subject to change at any time. When you need a token you should invoke acquireTokenSilent, it will return the token from the cache if available and if it returns InteractionRequired error you should catch this and invoke acquireTokenRedirect or acquireTokenPopup to resolve.

microsoft-github-policy-service[bot] commented 1 month ago

@renu10-dot 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.