[email protected]@azure/[email protected]@azure/[email protected]@azure/[email protected]@azure/[email protected]None
Every time I call acquireTokenSilent, msal makes calls to /v2.0/.well-known/openid-configuration and /oauth2/v2.0/authorize endpoints. Even if I call acquireTokenSilent multiple times in a single session (documentation recommends calling the API every time I need an access token to call an API), each call involves an outgoing call to those two endpoints, which negatively affects performance.
Documentation indicates that a cached access token should be used if one is available and valid. I see the access token in session storage, but it appears to be refreshed (instead of re-used) every time acquireTokenSilent is called.
No
I don't think so. I saw this behavior in 1.2.0 and 1.2.1, but didn't test earlier versions.
const msalConfig = {
auth: {
clientId: "<client id>",
authority: "https://<B2C authority path>/B2C_1_signup_signin",
validateAuthority: false,
redirectUri: "http://localhost:5000"
},
cache: {
cacheLocation: "sessionStorage"
}
};
const msalInstance = new Msal.UserAgentApplication(msalConfig);
Run this simple app which logs a user in and then requests an access token three times in a row with the same request/scopes. Notice that calls are made to openid-configuraiton and authorize endpoints for each acquireTokenSilent call and new tokens are returned each time, even though the tokens are valid for an hour.
<!DOCTYPE html>
<html lang="en">
<head>
<title>MSAL Repro</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
crossorigin="anonymous">
</head>
<body>
<div class="container" id="app">
<div class="row">
<h1>{{title}}</h1>
</div>
<div class="row">
<h3>Token 1</h3>
<p>{{token1}}</p>
</div>
<div class="row">
<h3>Token 2</h3>
<p>{{token2}}</p>
</div>
<div class="row">
<h3>Token 3</h3>
<p>{{token3}}</p>
</div>
</div>
</body>
<script src="https://alcdn.msauth.net/lib/1.2.1/js/msal.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vue.js" crossorigin="anonymous"></script>
<script>
async function getTokens() {
if (window.location.hash) {
return;
}
const msalConfig = {
auth: {
clientId: "<ClientId>",
authority: "https://<B2C Authority URL>/B2C_1_signup_signin",
validateAuthority: false,
redirectUri: "http://localhost:5000"
},
cache: {
cacheLocation: "sessionStorage"
}
};
const msalInstance = new Msal.UserAgentApplication(msalConfig);
var loginRequest = {
scopes: ["openid", "offline_access", "profile"]
};
await msalInstance.loginPopup(loginRequest);
if (msalInstance.getAccount()) {
var tokenRequest = {
scopes: ["https://<App URI>/user", "openid", "profile"]
}
// Each of these acquireTokenSilent calls results in calls to AAD. The tokens do not appear to be reused.
var tokenResponse1 = await msalInstance.acquireTokenSilent(tokenRequest);
app.token1 = tokenResponse1.accessToken;
var tokenResponse2 = await msalInstance.acquireTokenSilent(tokenRequest);
app.token2 = tokenResponse2.accessToken;
var tokenResponse3 = await msalInstance.acquireTokenSilent(tokenRequest);
app.token3 = tokenResponse3.accessToken;
}
}
const data = {
title: "MSAL Test",
token1: "",
token2: "",
token3: ""
};
const app = new Vue({
el: "#app",
data: data,
mounted: getTokens
});
</script>
</html>
I expect acquireTokenSilent to quickly return a cached token instead of getting a new token from AADB2C if one was previously retrieved and is still valid.
Tested with Chrome 79 and Chromium Edge 79
After a little more investigation, I found that this issue only repros when I have 'openid' or 'profile' in my token request scopes. If the scopes property is just https://<App URI>/user, I see the expected behavior of the cached token being reused. Even if I include multiple scopes in the request, the behavior is as expected if the scopes are just app scopes (rather than OIDC items like openid or profile).
It's not clear to me why having those scopes changes this behavior, but it at least narrows down what's going on.
@mjrousos Thanks for the follow up comment. I think this could be an issue with our scopes cache logic, which may be treating the second call as a new call since we check the token cache based on our scopes. Will track this and update once we confirm if this needs a fix from us.
Same issue in msal 1.1.3.
No issue in msal 0.2.3.
As is found openid is required scope.
https://openid.net/specs/openid-connect-basic-1_0.html#Scopes
So question is, do you handle this logic internally in the lib?
If it is true why didn't you highlight this in documentation?
If i remove openId and profile from scopes do i get correct behavior?
After a little more investigation, I found that this issue only repros when I have 'openid' or 'profile' in my token request scopes. If the scopes property is just
https://<App URI>/user, I see the expected behavior of the cached token being reused. Even if I include multiple scopes in the request, the behavior is as expected if the scopes are just app scopes (rather than OIDC items like openid or profile).It's not clear to me why having those scopes changes this behavior, but it at least narrows down what's going on.
The reason why a network request is made is because you are including scopes that span multiple resources (https://api-uri scope is for your custom resource and openid and profile is for graph). Access tokens are scoped per resource and the library caches access tokens accordingly. This means that when you try to acquire a token for scopes that span multiple resources it will always result in a cache miss. The solution is to make separate acquireTokenSilent calls for each resource. In your case a single call to acquireTokenSilent with your custom scope should get you the access token you need.
If this doesn't answer your question let us know.
Most helpful comment
The reason why a network request is made is because you are including scopes that span multiple resources (
https://api-uri scopeis for your custom resource andopenidandprofileis for graph). Access tokens are scoped per resource and the library caches access tokens accordingly. This means that when you try to acquire a token for scopes that span multiple resources it will always result in a cache miss. The solution is to make separateacquireTokenSilentcalls for each resource. In your case a single call toacquireTokenSilentwith your custom scope should get you the access token you need.If this doesn't answer your question let us know.