microsoft-authentication-library-for-js
microsoft-authentication-library-for-js copied to clipboard
AcquireTokenSilent not working as expeced
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
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)
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
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.
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 .
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.
@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.