microsoft-authentication-library-for-js icon indicating copy to clipboard operation
microsoft-authentication-library-for-js copied to clipboard

AcquireTokenSilent not working as expeced

Open renu10-dot opened this issue 1 year ago • 3 comments

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)

  • [ ] Sent
  • [ ] Pending

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)

renu10-dot avatar Jul 11 '24 04:07 renu10-dot

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

tnorling avatar Jul 15 '24 18:07 tnorling

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 avatar Jul 19 '24 04:07 renu10-dot

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

main.js:1 [Mon, 22 Jul 2024 12:00:10 GMT] : [0af849cd-6931-4eea-87a3-d34dd8907476] : @azure/[email protected] : Info - in acquireToken call in auth-code client main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/[email protected] : Info - BrowserCacheManager: addTokenKey - idToken added to map main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/[email protected] : Info - BrowserCacheManager: addTokenKey - accessToken added to map main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/[email protected] : Info - BrowserCacheManager: addTokenKey - refreshToken added to map main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/[email protected] : Info - Emitting event: msal:loginSuccess main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/[email protected] : Info - Emitting event: msal:handleRedirectEnd main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 12:00:11 GMT] : @azure/[email protected] : 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/[email protected] : Info - CacheManager:getIdToken - Returning id token

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

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

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

main.js:1 Login Silent called

main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : @azure/[email protected] : Info - Emitting event: msal:acquireTokenStart main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : @azure/[email protected] : Info - CacheManager:getAccessToken - No token found main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : @azure/[email protected] : Info - CacheManager:getRefreshToken - returning refresh token main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : [29c332b8-63b8-4e49-882e-46ef9029150d] : @azure/[email protected] : 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/[email protected] : Info - Emitting event: msal:acquireTokenFromNetworkStart main.js:1 [Mon, 22 Jul 2024 14:06:49 GMT] : @azure/[email protected] : 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/[email protected] : Info - Emitting event: msal:acquireTokenFailure main.js:1 TokenResponse:- B__zone_symbol__state: true__zone_symbol__value: 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 .

renu10-dot avatar Jul 22 '24 14:07 renu10-dot

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.

tnorling avatar Aug 14 '24 22:08 tnorling

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