Open janandreschweiger opened 3 years ago
I experience the exact same issue. Could you please have a look on this one @tnorling?
What happens if you provide forceRefresh: true
on the acquireTokenSilent
request object?
Thanks for your answer @tnorling.
This would probably resolve the issue, as I get the following additional log:
@azure/msal-browser@2.18.0 : Info - Emitting event: msal:acquireTokenFromNetworkStart
This issues normally occurs after 1h, so we would have to wait. But please have a look at my logs posted above. The issue is resolved once the acquireTokenFromNetworkStart log indicates that a new token is fetched.
I suspect this is happening when the access token has a longer validity than the id token, if that's the case we'll need to re-evaluate how we do cache lookups today. We currently only check the access token validity because we made a naive assumption that the cached idToken would always have at least as much time left as the access token.
If setting forceRefresh
resolves this please use that as a workaround for now until we're able to put out a fix.
@hectormmg will follow up as the current on call engineer
Thank you so much @tnorling for your help! I will write you back, if this resolved the issue.
Yes, this option resolved my issue for now. Nevertheless, it would be nice to use cached tokens again. Thanks for your help @hectormmg!
@janandreschweiger thanks for updating us, I'll start looking into factoring in ID token expiration into the cache logic.
I am seeing the same issue in my apps. I chose a variation on the workaround to dynamically set the forceRefresh
option based on the id_token being expired. If you have LOTS of API calls using the id_token leveraging the cache is very important..
const idTokenClaims: { exp?: number } = this.authService.instance.getActiveAccount().idTokenClaims;
const forceRefresh = (new Date(idTokenClaims.exp + 1000) < new Date());
this.authService.instance
.acquireTokenSilent({
forceRefresh: forceRefresh
}).then((authResponse) => {
// do something with authResponse.idToken
});
Looking forward to fix in MSAL library.
@janandreschweiger @joshpitkin @nils-tahler Any specific reasons you are validating the id token, instead of an access token (e.g. using a custom scope)?
That is a good question. In my situation we have multiple web apps using the library that are connecting with several different WebAPIs and passing in the id_token as the Auth credential. It has been that way for years, starting with ADAL.js and recently supporting MSAL also. All of our apps are internal so we are less concerned about setting up unique permissions / tokens per API as it just adds complexity and maintenance. We only need to validate the user's identity (username) and that the token came from a valid source. It is on the roadmap to migrate some things over to accessTokens, however we also have another complex internal system for controlling access to different resources and don't want to maintain it in duplicate with the accessToken mechanism..
Same here. We have a daemon for Microsoft Graph that retrieves and stores all the data of an organization in our database. Therefore, we only need to authenticate the user.
This issue requires attention from the MSAL.js team and has not seen activity in 5 days. @pkanher617 please follow up.
I believe I am having a similar issue. We need to verify that users sending requests to our API's have permissions. Until recently, we have been using the ID token without issue (PKCE Flow). We tried switching to the access token, but the signature on the token cant be verified because it has a nonce and is specific for graph. Its been a very circular process in my googling....users who cant verify access token signatures say use idToken, but here it sounds like its not best practice to use idToken and accessToken is preferred. Neither works for the above reasons.
@its-miller-time In your scenario (and in most scenarios) you should be using an access token. There are very few scenarios where we would recommend using an idToken for anything other than getting basic information about the user (e.g. email address, name, etc.). Anytime you need to validate the user has permission to access something you should use an access token.
The reason your access token is scoped to MS Graph is because you're either requesting MS Graph scopes (User.Read for instance) or you're not requesting scopes at all (MSAL.js will default to openid
and profile
which returns a Graph access token if no other, more specific scopes are requested). To acquire an access token for your own resource you should create a custom scope on your app registration and request that scope when acquiring a token with MSAL.
@tnorling thanks for the reply. Yes we are requesting graph scopes. We were wanting to simply pass the headers along and validate the token in our API but it doesn't sound like that's possible? From what I gather the recommended/only solution would be to make an app registration specifically for our api's and request an additional token? Or will a custom scope allow us to use the same access token?
On a side note: the ID token had been working fine for a year prior to this. Did something change in the ID token lifetime to where it expires sooner now?
@its-miller-time
We were wanting to simply pass the headers along and validate the token in our API but it doesn't sound like that's possible? From what I gather the recommended/only solution would be to make an app registration specifically for our api's and request an additional token? Or will a custom scope allow us to use the same access token?
Access tokens are scoped to a single resource. If you need to access MS Graph APIs in addition to your own API you need 2 access tokens. However, this does not mean you need more than 1 app registration, you can register as many API permissions as you need on a single app registration.
On a side note: the ID token had been working fine for a year prior to this. Did something change in the ID token lifetime to where it expires sooner now?
The issue here is that AAD changed access tokens lifetimes from a static 60 minutes to a variable 60-90 minutes and MSAL.js only checks access token lifetimes. Id tokens still have a static 60 minute lifetime.
@its-miller-time In your scenario (and in most scenarios) you should be using an access token. There are very few scenarios where we would recommend using an idToken for anything other than getting basic information about the user (e.g. email address, name, etc.). Anytime you need to validate the user has permission to access something you should use an access token.
I've also run into this bug on my application, but I was just curious about why you always want to use access tokens over idTokens? If your application is implementing role based authentication, you can store the roles with your Azure App Registration and have them returned as claims, but you can also store that information with your user account in your database. It means that you might have to do an extra db fetch at the api layer to get the user's role, but it shouldn't be any less secure if I'm understanding things correctly. I've generally found that it simplifies your application if Msal is just handling identity verification, and your application manages all permissions and permission validation separately.
Hi @tnorling, any updates so far?
We currently use @joshpitkin's workaround. However, we had to change the '+' to a '*'.
Unfortunately I have no updates and no ETA as this will need to be prioritized against our other efforts. Our recommendation is still to use access tokens whenever possible and if there's anything preventing you from using access tokens please do let us know and we would be happy to help you resolve whatever issues you are facing.
I've been using the idToken because it lets me map AD-group memberships to application roles, and those claims are automatically available on both the client and the server. Is there a way for me to do that with access tokens?
@markusberg Yes, the role
and group
claims are also available on access tokens. You just need to make sure the audience of the access token you request is that of your application. You can do this by registering a custom scope on your app registration.
I see the same issue with idTokens not being refreshed and I'm running @angular/msal-browser 2.21.
While I appreciate that access tokens are recommended, in my service, the backend validates the idToken w/ .NET Core middleware and then uses a custom Role based system. The design is like this as pre-2.x versions of MSAL worked without issue to retrieve idTokens. No extra scopes/consent were required and we could simply pass ClientId to get the idToken. Additional scopes/resource access, since we use a custom Role-based system, are not needed. Also, since I'm using "First Party" AAD (4-5 instances), it becomes painful to add custom scopes since there are encryption requirements when additional scopes are added. It's a fair number of hoops to jump through to make something work, tbh.
At any rate, it has become somewhat problematic in that the idToken will be expired, won't be refreshed, and then 401 errors are returned on the API calls. It seems the only work-around for the users has been to force a logout in the AAD tenant. I may try "forceRefresh" in conjunction with checking the expiry, but this does potentially cause quite a bit more traffic back to the AAD tenant. On a related note, it would be nice if it were possible to tell MsalInterceptor to use the idToken instead of accessToken attaching to auth headers.
I am seeing the same issue in my apps. I chose a variation on the workaround to dynamically set the
forceRefresh
option based on the id_token being expired. If you have LOTS of API calls using the id_token leveraging the cache is very important..const idTokenClaims: { exp?: number } = this.authService.instance.getActiveAccount().idTokenClaims; const forceRefresh = (new Date(idTokenClaims.exp + 1000) < new Date()); this.authService.instance .acquireTokenSilent({ forceRefresh: forceRefresh }).then((authResponse) => { // do something with authResponse.idToken });
Looking forward to fix in MSAL library.
Unfortunately, this approach doesn't work (at least for me): instance.getActiveAccount() always returns null, so i tried account from hook ( const msAccount = useAccount(accounts[0] || {}) where accounts is object array from useMsal() hook), but it always keeps the first acquired idToken and doesn't refresh data for account after acquireTokenSilent call with forceRefresh:true, so after first token is expired it always set forceRefresh to true.
here is my code snippet, maybe i'm doing something wrong:
//...
const defaultScopes= ["openid", "profile"];
//...
const {instance, accounts} = useMsal();
const msAccount = useAccount(accounts[0] || {});
const buildRequestMethod = async ()=> {
try {
const request = async (apiCallConfig) => {
const { exp } = msAccount.idTokenClaims;
//const activeAcc = instance.getActiveAccount() - always null
const expDate= new Date(exp * 1000), now = new Date();
const forceRefresh = (expDate<now);
const tokenResp = await instance.acquireTokenSilent({scopes:defaultScopes, account: msAccount, forceRefresh: forceRefresh });
const jwt = tokenResp.idToken;
//...
The initial assumption we made when we designed the library is the uniformity between expiry times for id_token and access_token. However, this is not true in all cases and we need to enhance the library to support these various expiry times. Added this to our internal backlog.
However, please note that this would need some redesign from our end and hence will take some time.
Hi, we are facing this exact same issue. The workaround by forcing a refresh is suboptimal for various reasons :- \ Is there any eta for an update for this?
Dear Team , I am also facing same issue , my idToken is not gettng renewed after expiry , it works when i set forceRefresh as true , but still some user face authentication issue , in Vue2 we use ADAL library , but migrating to vue3 adal got deprecated so we started using msal , but we are facing lot of issues while using msal . Please help.
Hi Everyone,
We have the same challenge. We use the id token to call a vendor API. But it expires around 65 minutes. What we do has a few steps.
I did notice that MSAL library check only access token's lifetime which expires around 86 minutes, having a longer life time than id token which has around 65 minutes. the 21 minutes in between is causing a 403 error when we are trying to call the vendor API.
As tnorling stated on 11/1/2021:
I suspect this is happening when the access token has a longer validity than the id token, if that's the case we'll need to re-evaluate how we do cache lookups today. We currently only check the access token validity because we made a naive assumption that the cached idToken would always have at least as much time left as the access token.
If MSAL could check both id token and access token, or whichever has shorter lifetime, then get a new set of tokens, which will solve this issue. Hope this can be worked on in the near future.
Thanks!
cc @EmLauber. We are aware of this issue and there is no easy solution here. I would like us to track this internally if not there already.
@sameerag @EmLauber Thank you very much!
I'm going to drop a link on ID Tokens as a reference if folks have additional questions on ID Token usage.
https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens
For this particular issue we're going to have to make an API/interface change to address this to request explicit token types. For instance, you should be able to just request an Access Token without a Refresh Token, or just an ID Token. We've got an internal item tracking for this work for a future major release. In the interim, this won't be changed for MSAL.js v2.x.y releases.
If you look into this, I suggest that you also consider the use case where there is no accessToken at all. Currently, when not using any custom scopes, so that the identity acccess managment (Azure B2C in our case) won't issue an accessToken, the acquireTokenSilently method doesn't handle that properly. It should handle cases where there is only an idToken available, and no accessToken.
I'm running into this with
@azure/msal-browser@3.7.0 @azure/msal-common@14.6.0 @azure/msal-react@2.0.9
When I dump the tokens returned from acquireTokenSilent, I see that the ID token has a shorter lifespan than the access token:
Access Token:
Issued: Thu Mar 07 2024 10:28:46 GMT-0800 (Pacific Standard Time)
Expires: Thu Mar 07 2024 11:44:10 GMT-0800 (Pacific Standard Time)
ID Token:
Issued: Thu Mar 07 2024 10:28:46 GMT-0800 (Pacific Standard Time)
Expires: Thu Mar 07 2024 11:33:46 GMT-0800 (Pacific Standard Time)
This call was made at 11:33:37 GMT-0800, so it's also possible that the ID token expiry was checked, but in a simple way that did not recognize it as about to expire.
edit: Looked at the source, and it doesn't check the id token at all.
Having the same issue while using the latest msal packages. ID token in not refreshed when calling acquireTokenSilent()
even if it has expired.
"@azure/msal-browser": "^3.17.0", "@azure/msal-react": "^2.0.19",
Version:"@azure/msal-browser": "^3.15.0"
Just adding to the above that I'm also running into the ID token refresh issue when configured for NAA using createNestablePublicClientApplication
with the following config:
const msalConfig = {
auth: {
clientId: applicationId,
authority: 'https://login.microsoftonline.com/common',
supportsNestedAppAuth: true,
},
};
However, the current workaround to use forceRefesh doesn't appear to resolve the issue, and having a quick look through the source code, I can't seem to see if NAA supports forceRefresh?
According to MS documentation
The default lifetime of an access token is variable. When issued, an access token's default lifetime is assigned a random value ranging between 60-90 minutes (75 minutes on average)
So if I want the ID token (which always expires after 1 hour) to always be renewed at most 5 minutes before its expiration, I can just set tokenRenewalOffsetSeconds
to 35 minutes:
const msalConfig = {
// ...
system: {
// acquireTokenSilent only looks at the expiration time of the access token to determine when it needs
// to refresh the tokens (because it wrongly assumes all tokens have the same lifetime).
// Microsoft changed their backend and the access token now have a random lifespan between 60 and 90 minutes
// while the ID token has a fixed 1 hour lifetime (at least by default)
// This means acquireTokenSilent can return expired ID tokens from cache for at most 30 minutes
// To work around this, we set the renewal offset to 35 minutes before the accessToken expires
// which will translate to 5 to 35 minutes before the ID token expires
// Ref: https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/4206
tokenRenewalOffsetSeconds: 35 * 60,
}
};
Core Library
MSAL.js v2 (@azure/msal-browser)
Core Library Version
2.18.0
Wrapper Library
Not Applicable
Wrapper Library Version
None
Description
Hi Micrsoft communiy,
We have a react app, which implements your library as described below. We only use it to get an id-token for validating the Microsoft account on our server. Unfortunately, after an hour the id-tokens that are returned by the acquireTokenSilent function are expired.
We have tried to set tokenRenewalOffsetSeconds to 300, but this doesn't resolve the issue. If we logout, clear the cache or refresh the site a few times, we get a valid token again. But this is unfortunately, very unpleasant for our users.
We get a lot of customer complaints on a daily basis, because of this issue. Please help us. We are thankful for any suggestions. Thanks!!
Error Message
No response
Msal Logs
Here are some logs. Please ignore the "DEBUG" in front of the messages.:
MSAL Configuration
Relevant Code Snippets
Reproduction Steps
Expected Behavior
The id-token should be refreshed before it expires.
Identity Provider
Azure AD / MSA
Browsers Affected (Select all that apply)
Chrome, Firefox
Regression
No response
Source
External (Customer)