Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loop when refresh token does not return an id token #1188

Closed
wima75 opened this issue Jan 27, 2022 · 13 comments
Closed

Loop when refresh token does not return an id token #1188

wima75 opened this issue Jan 27, 2022 · 13 comments
Labels
bug For tagging faulty or unexpected behavior.

Comments

@wima75
Copy link

wima75 commented Jan 27, 2022

We use this library in our angular 12 project with azure ad b2c and code flow. After the login we get an access-token and an id-token. After one hour they expires. Then the library does a refresh-token request. In the response of this request the id-token is missing. Because the id-token is expired, it does a new refresh-token request immediately, the id-token is still missing. This loop never ends.

@jeroenheijmans
Copy link
Collaborator

Can you help us reproduce this issue? A minimal sample of sorts, or at the least redacted responses, would be helpful. Otherwise it's a bit down to guessing at the root cause.

You could try and reconfigure my sample repo against your IDS and see if it happens. If so then I presume it's specific to your IDS, as I haven't seen this problem with IdentityServer4 at all thus far.

@jeroenheijmans jeroenheijmans added more-info-needed Please provide a minimal example (e.g. at stackblitz.com) which demonstrates the issue question For tagging support requests and general questions. labels Jan 27, 2022
@wima75
Copy link
Author

wima75 commented Jan 27, 2022

I think the problem is, that IdentityServer4 does include the id-Token in the refresh-response, but azure ad b2c does not.

@wima75
Copy link
Author

wima75 commented Jan 28, 2022

Some more information:
The Id-Token in the refresh token response is optional: https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse

This answer says, that the Id-Token must not be refreshed: https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse

Here is the function, where the timers for the token refresh will be set:

protected setupExpirationTimers(): void {

If the refresh token response does not include an Id-Token and the Id-Token is expired, it will be invalid (line 425). Then a new timer will be set. But because the next response does still not include a Id-Token, it is still invalid and e new timer will be set. This is the endless-loop.

@jeroenheijmans
Copy link
Collaborator

Thanks for the links and the pointers to the code. Clear description, and that scenario makes sense. If the id token is not included (as possible per the spec) then the timers should behave accordingly.

In #1185 a similar root cause for a very different issue was noted as well. Very maybe even that issue gets resolved too if we fix things here?

@jeroenheijmans jeroenheijmans added bug For tagging faulty or unexpected behavior. and removed question For tagging support requests and general questions. more-info-needed Please provide a minimal example (e.g. at stackblitz.com) which demonstrates the issue labels Jan 28, 2022
@lgadola
Copy link

lgadola commented Feb 2, 2022

I have come across this behaviour in a different scenario.
I use ABP framework which uses this library and IdentityServer.
Reported the issue to the ABP guys and they advised to downgrade rxjs to below 7. When I did so, the loop after the login disappeared.
The error can be reproduced using my git repository which is still online

@pkfms
Copy link

pkfms commented Apr 16, 2022

I also noticed this bug using ABP v5.2.0 and upgrading to rxjs 7.5.5. is there any workaround other than downgrading rxjs? is the problem in this library or with abp?

@silslt
Copy link

silslt commented May 16, 2022

Hi,
I use angular-oauth2-oidc 12.1.0 with Authorization Code PKCE flow for an Angular 10.1.2 project.

When the refresh works, I get this kind of log in the console:

  • OAuthInfoEvent {type: 'token_expires', info: 'access_token'}
  • refresh tokenResponse {access_token: 'AAAA', refresh_token: 'BBBB', scope: 'profile', token_type: 'Bearer', expires_in: 358}
  • OAuthInfoEvent {type: 'token_received', info: null}
  • OAuthInfoEvent {type: 'token_refreshed', info: null}

When the refresh seems to fail, I notice an endless loop on the refresh with this kind of log in the console that pops every ~2 seconds (the timeoutFactor is not respected anymore):

  • refresh tokenResponse {access_token: 'CCCC', refresh_token: 'DDDD', scope: 'profile', token_type: 'Bearer', expires_in: 358}
  • OAuthInfoEvent {token_received, info: null}
  • OAuthInfoEvent {token_refreshed, info: null}
  • OAuthInfoEvent {token_expires, info: 'id_token'}

I notice the order of the events is different: token_expires pops the 1st when the refresh works (which seems normal).
When it fails, the event order is different.
Also, the token_expires event is about access_token when the refresh works and about id_token when if it failed and when the endless loop starts.

I use the following conf points:
export const authConfig: AuthConfig = {
issuer: 'XXX',
logoutUrl: 'XXX',
redirectUri: 'XXX',
redirectUriAsPostLogoutRedirectUriFallback: false,
useSilentRefresh: false, // Not needed for Authorization Code PKCE as it uses a refresh token
timeoutFactor: 0.75,
clientId: 'XXX',
responseType: 'code', // Needed for Authorization Code PKCE
disablePKCE: false, // (Already false by default)
scope: 'profile',
showDebugInformation: true
};

I also tried to add the scope offline_access but the loop still happens. I think I receive a refresh token even without adding this scope.

What can be the cause of that refresh loop?

Thanks.

@silslt
Copy link

silslt commented May 16, 2022

@jeroenheijmans, is the loop described in my previous post a bug of angular-oauth2-oidc library as you tagged this theme "bug"?

If yes, is there any work around to avoid this loop?
@wima75, how did you handle the problem finally?

@jeroenheijmans
Copy link
Collaborator

I labelled it a 'bug' after getting the reproducible scenario from the Original Poster of this thread. I've not looked into your post/repro yet, don't know if it's the same thing or something different?

@silslt
Copy link

silslt commented May 16, 2022

I don't know if the logs I get are the same as the original poster.
However, I tried to put some details (logs, versions, configuration) and the final result seems to be the same.

I edited my 1st post of this thread because I realized than depending on the lifetime of the tokens (authorization code, refresh token and access token) set in the IDP, the refresh can fail directly at the first refresh or with other values, after a certain amount of refreshs.

@jeroenheijmans , what do you think?
Very interested if there's correction or a work around.

@silslt
Copy link

silslt commented May 16, 2022

If it can help, for information, in our AuthConfig, I updated the scope attribute from:
scope: 'profile',
to
scope: 'openid profile email', (as it is on IDP side also)
and the behaviour seems better because now, when the timeoutFactor is reached, two token_expires events are thrown: one about the access_token and one about the id_token (whereas there was only one event about the access_token when only using the scope profile).
Just after, the console shows: refresh tokenResponse with:
{
"access_token": "AAA",
"refresh_token": "BBB",
"scope": "openid profile email",
"id_token": "CCC",
"token_type": "Bearer",
"expires_in": 3598
}

Then: 2 OAuthSuccessEvent, one about token_received and one about token_refreshed are displayed in the console.

@jeroenheijmans, there's no endless loop anymore and the refresh are done at the expected times.

@bitbike
Copy link

bitbike commented Jun 30, 2022

Having the same issue. When the token is refreshed, the id_token is missing in the answer. Even with a changed scope it does not deliver an id_token.

Afaik the timer for the access_token is reset when the silentrefresh occurs. The one for the id_token remains untouched(since it is missing in the response). Therefore it tries to refresh the token every second (debounceTimer of 1000 millis) and fails successfully refreshing the id_token.

My workaround was to only listen to the access_token expiration.

I did that by oauthService.setupAutomaticSilentRefresh({}, 'access_token'); and therefore only watch the access_token.

It worked for me, aslong as I could not identify the problem of the missing id_token.

@manfredsteyer
Copy link
Owner

This next version will have a config property disableIdTokenTimer.

Also, you could try to not use the automatic timers but call refreshToken() directly in your code when needed, e. g. in an interceptor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug For tagging faulty or unexpected behavior.
Projects
None yet
Development

No branches or pull requests

7 participants