AcquireTokenPopup for someone other than logged on user
See original GitHub issueLibrary
-
msal@1.x.xor@azure/msal@1.x.x
Description
This question relates to msal@1.2.x even though we have SPA which uses @azure/msal-angular@0.1.4-beta.2 and which we’re trying to upgrade to @azure/msal-angular@1.0.0-beta.2 (therefore bringing in the msal@1.2.x peer dependency).
Previously when we were using @azure/msal-angular@0.1.4-beta.2, we took a dependency of @azure/msal-angular/msal@0.2.4. Our use case worked with @azure/msal-angular/msal@0.2.4 and it no longer works with msal@1.2.x.
Our use case is as follows. There is a main logged on user in our app who performs various activities in the app and at some point needs to perform a privileged operation that requires elevated permissions. The user does not have those permissions and needs to call over a manager to type in his or her credentials. Barring the inherent risk of the manager typing his or her credentials on a user workstation (which we plan on curing by enabling 2FA for the manager), this scenario worked fine when we used acquireTokenPopup in @azure/msal-angular/msal@0.2.4 using the following code:
const user = this.authService.getUser();
return from(this.authService.acquireTokenPopup(
['api://[our_api_uri]/user_impersonation`],
'https://login.microsoftonline.com/[tenantId],
user,
'prompt=login&forceRefresh=true'
));
The manager would sign in using his or her own credentials and this would give us an access token which we then used to call our API to execute the privileged operation as the manager. This did not affect the currently logged on user and after the privileged operation was executed, the user would continue performing functions in the app as himself/herself. The manager therefore provided an access token which was used one time, just for the privileged operation.
After upgrading to msal@1.2.2-beta.0 which is a peer dependency of @azure/msal-angular@1.0.0-beta.2, we needed to change the above code to the following:
const account = this.authService.getAccount();
return from(this.authService.acquireTokenPopup({
scopes: [environment.api.audience + '/user_impersonation'],
account: account,
prompt: 'login',
forceRefresh: true
})).pipe(
map(authResponse => authResponse.accessToken)
);
Seems like a simple enough upgrade, right? Unfortunately not. The acquireTokenPopup returns a null accessToken in the updated code. After digging around and debugging the code inside UserAgentApplication, I found that even though Azure AD was returning an accessToken for the correct account (the manager) in the URL hash, it was being discarded in the following code within the saveTokenFromHash method in UserAgentApplication:
// Process access_token
if (hashParams.hasOwnProperty(ServerHashParamKeys.ACCESS_TOKEN)) {
this.logger.info("Fragment has access token");
// retrieve the id_token from response if present
if (hashParams.hasOwnProperty(ServerHashParamKeys.ID_TOKEN)) {
idTokenObj = new IdToken(hashParams[ServerHashParamKeys.ID_TOKEN]);
response.idToken = idTokenObj;
response.idTokenClaims = idTokenObj.claims;
} else {
idTokenObj = new IdToken(this.cacheStorage.getItem(PersistentCacheKeys.IDTOKEN));
response = ResponseUtils.setResponseIdToken(response, idTokenObj);
}
So in the above, hashParams[ServerHashParamKeys.ACCESS_TOKEN] is not used at all in the resultant response. What we get back is actually a response with the idToken set for the currently logged on user instead of a response with an accessToken for the manager.
The questions are therefore:
- Is this a bug, and if not,
- Is there a way that we can get an access token for the manager using
msal@1.2.xwithout affecting the currently logged on user and also without affecting any associated session or local storage for the user?
Issue Analytics
- State:
- Created 4 years ago
- Comments:8 (5 by maintainers)

Top Related StackOverflow Question
@jasonnutter Apologies for the late response, I have been under pressure to meet certain deadlines.
I did some more digging and narrowed down the issue even further by debugging both the old version and new version of
UserAgentApplication.In the old version 0.2.4 of the
processCallbackmethod ofUserAgentApplication, the access token in our case is obtained regardless of what thesaveTokenFromHashdoes just before that. Then, the token received callback is called with the correct token. This is why our scenario worked in the past.In the newer versions, the
AuthResponseclass was introduced and reliance is placed onsaveTokenFromHashpopulating theAuthResponsecorrectly. The only “post processing” thatprocessCallbackdoes aftersaveTokenFromHashis called, is that the token type is set in the response. TheprocessCallbackmethod no longer tries to get the access token from the hash anymore.So my suggestion would be to update the
saveTokenFromHashmethod, specifically the block of code that I pasted previously, to not only try to get the ID token from the hash but also get the access token from the hash as well. The following code:Could change to:
After the above change, the response should contain the access token as well and it would no longer be discarded.
As a separate issue, I do not think that the handling of the ID token is quite correct in the existing code because the code falls back to using the existing ID token in the cache if the hash does not contain one. In our case someone other than the currently logged on user enters their credentials when the
acquireTokenPopupmethod is called, yet we get back anAuthResponseresponse with the ID token set as the currently logged on user. Perhaps the more correct behavior is to keep the ID token asnullin theAuthResponseif an ID token isn’t present in the hash? As mentioned though, this is a separate issue that should be debated.The issue has been fixed with
msal@1.2.2-beta.2, thanks @jasonnutter!