Angular-auth-oidc-client: Refresh response without an id token breaks mechanism

Created on 2 Mar 2021  路  12Comments  路  Source: damienbod/angular-auth-oidc-client

Describe the bug
When I configure the library with Auth code + refresh token, and the authorization server doesn't return an id_token on refresh, the refresh mechanism breaks.

To Reproduce
Config:

      responseType: 'code',
      silentRenew: true,
      useRefreshToken: true

First call

POST:
{
      grant_type: authorization_code
      client_id: myclientid
      code_verifier: 123456
      code: abcdef
      redirect_uri: http://localhost:4200/
}
RESPONSE:
{
      access_token: "blabla"
      expires_in: 599
      id_token: "blabla"
      refresh_token: "blabla"
      scope: "blabla"
      token_type: "Bearer"
}



md5-c465d2a68e467bfb72d7885e6e1d2323



POST:
{
grant_type: refresh_token
client_id: myclientid
refresh_token: blabla
}



md5-051ca490a5175132a9786da40b0dbb0c



RESPONSE:
{
      access_token: "blabla"
      expires_in: 599
      refresh_token: "blabla"
      scope: "blabla"
      token_type: "Bearer"
}



md5-a5b3c8cf6e2721d682084f807816759a



      Checking: silentRenewRunning: false id_token: false userData: true
      silent renew, periodic check finished!
      Checking: silentRenewRunning: false id_token: false userData: true
      silent renew, periodic check finished!

Expected behavior
According to spec, I think it should be acceptable for an id_token not to be in the response of the refresh.
https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse

Upon successful validation of the Refresh Token, the response body is the Token Response of Section 3.1.3.3 except that it might not contain an id_token.

Additional context
Apologies if it's an issue in my config :)

bug

All 12 comments

Hi @sive87

This could be a problem with the implementation, not sure if I have implemented this to require an _id_token on every refresh.

Will check, can you post your configuration as well?

Out of interest, what IDP do you use?

Greetings Damien

@damienbod
It's an in-house home made IDP (which is why I double-checked the spec :) to be sure)

Looks like it should work, going through the code, what fails? any logs in the browser console?

I might need to disable the id_token expired time check, I know people want this.

Greetings Damien

Has id_token expired: false, 1614800942000 > 1614758340000
Has access_token expired: false, 1614758343000 > 1614758340000
silent renew, periodic check finished!
Checking: silentRenewRunning: false id_token: true userData: true
Has id_token expired: false, 1614800942000 > 1614758344000
Has access_token expired: true, 1614758343000 > 1614758344000
starting silent renew...

Wanted to read 'xxx_storageCustomRequestParams' but nothing was found
BEGIN refresh session Authorize
RefreshSession created. adding myautostate: 0a6272b42abba7b587bffe2f55816af725iRVdKDS
found refresh code, obtaining new credentials with refresh code
logger.service.ts:55 token refresh response:  
{access_token: "xxx", token_type: "Bearer", expires_in: 599, refresh_token: "xxx", scope: "scope1 scope2"}
history clean up inactive

{access_token: "xxx", token_type: "Bearer", expires_in: 599, refresh_token: "xxx", scope: "scope1 scope2", 鈥
authorizedCallback created, begin token validation
logger.service.ts:55 Getting signinkeys from  https://blabla/oauth2/api/jwks
zone.js:3324 XHR finished loading: POST "https://blabla/oauth2/api/token".
No id_token found, skipping id_token validation
token '' is not valid --> token falsy
Code Flow active, and no at_hash in the id_token, skipping check!
AuthorizedCallback token(s) validated, continue
storing the accessToken
SafeSubscriber._next: NewAuthorizationResult 
{type: 4, value: {鈥}
silent renew, periodic check finished!
XHR finished loading: GET "https://blabla/oauth2/api/jwks".
Checking: silentRenewRunning: false id_token: false userData: true
silent renew, periodic check finished!
Checking: silentRenewRunning: false id_token: false userData: true
silent renew, periodic check finished!
Checking: silentRenewRunning: false id_token: false userData: true
silent renew, periodic check finished!

token '' is not valid --> token falsy
Is the only error.

Whilst testing: If I add the id_token to the storage.authnResult, then the mechanism works again and properly performs checks on expiration

will fix this, see the bug, thanks for your patience.

Hello,

I restart the issue.
I am using version 11.6.4 but I have the exact same problem.
When I do the first "Refresh" my IDP doesn't return the id_token and after that the library doesn't do the checking anymore.

Is it a configuration problem?

Here are my console logs.

STS server: http://xxxxx
currentUrl to check auth with:  http://localhost:4200/?code=xxx&state=xxxxx
 Wanted to read 'xxxxx:client_storageSilentRenewRunning' but nothing was found
running validation for callback http://localhost:4200/?code=xxx&state=xxxxx
 Wanted to read 'xxxxx:client_storageSilentRenewRunning' but nothing was found
Angular is running in development mode. Call enableProdMode() to enable production mode.
 {id_token: "xxxx", access_token: "xxxxxx", refresh_token: "xxxxxx", scope: "openid profile", at_hash: "xxxx",聽鈥
 authorizedCallback created, begin token validation
Getting signinkeys from  xxxxxxx/api/jwks
[WDS] Live Reloading enabled.
 validate id token iat max offset 1000 < 10000
 Has id_token expired: false, 1616558901000 > 1616515702000
 at_hash from the server:xxxxx
 at_hash client validation not decoded:xxxx
 AuthorizedCallback token(s) validated, continue
 xxxx
 storing the accessToken
 Wanted to read 'xxxxx:client_userData' but nothing was found
Received user data {sub: "xxxx", family_name: "xxxx", given_name: "xxxx", ...}
accessToken xxxxx
 Has id_token expired: false, 1616558901000 > 1616515713000
 Has access_token expired: false, 1616515731000 > 1616515713000
 persisted id_token and access token are valid
 starting token validation check every 4s
 checkAuth completed fired events, auth: true
app authenticated true
Current access token is 'xxxx'
 Wanted to read 'xxxxxx:client_storageSilentRenewRunning' but nothing was found
 Checking: silentRenewRunning: false id_token: true userData: true
 Has id_token expired: false, 1616558901000 > 1616515717000
 Has access_token expired: false, 1616515731000 > 1616515717000
 silent renew, periodic check finished!
 Checking: silentRenewRunning: false id_token: true userData: true
 Has id_token expired: false, 1616558901000 > 1616515721000
 Has access_token expired: false, 1616515731000 > 1616515721000
 silent renew, periodic check finished!
 Checking: silentRenewRunning: false id_token: true userData: true
 Has id_token expired: false, 1616558901000 > 1616515725000
 Has access_token expired: false, 1616515731000 > 1616515725000
 silent renew, periodic check finished!
 Checking: silentRenewRunning: false id_token: true userData: true
 Has id_token expired: false, 1616558901000 > 1616515729000
 Has access_token expired: false, 1616515731000 > 1616515729000
 silent renew, periodic check finished!
 Checking: silentRenewRunning: false id_token: true userData: true
 Has id_token expired: false, 1616558901000 > 1616515733000
 Has access_token expired: true, 1616515731000 > 1616515733000
 starting silent renew...
 Wanted to read 'xxxxx:client_storageCustomRequestParams' but nothing was found
 BEGIN refresh session Authorize
 RefreshSession created. adding myautostate: 5634409ee26c52311001d25e13a625d9fcF2LgPnN
 found refresh code, obtaining new credentials with refresh code
token refresh response:  {access_token: "xxxx", scope: "openid profile", refresh_token: "xxxx", expires_in: 29, token_type: "Bearer"}
 history clean up inactive
 {access_token: "xxx", scope: "openid profile", refresh_token: "xxx", expires_in: 29, token_type: "Bearer",聽鈥
 authorizedCallback created, begin token validation
Getting signinkeys from  https://xxxxxxx/oauth2/api/jwks
 No id_token found, skipping id_token validation
 AuthorizedCallback token(s) validated, continue
 xxxxx
 storing the accessToken
 silent renew, periodic check finished!
 Checking: silentRenewRunning: false id_token: false userData: true
 silent renew, periodic check finished!
 Checking: silentRenewRunning: false id_token: false userData: true
 silent renew, periodic check finished!
 Checking: silentRenewRunning: false id_token: false userData: true
 silent renew, periodic check finished!
 Checking: silentRenewRunning: false id_token: false userData: true
 silent renew, periodic check finished!
 Checking: silentRenewRunning: false id_token: false userData: true

Hi @FiReBlUe45 tested this and it works. Could you post a example of what you do? and more info, STS etc. Maybe a stackblick would be good.

Greetings Damien

Hi @damienbod,

It is a private IDP server and accessible only from a VPN.
Here is my project on stackblitz: https://stackblitz.com/edit/angular-ivy-tqdsrf

I can provide a video from my browser with the console to show you.

Greetings,

Hi @damienbod

I meant to reply earlier but it doesn't work yet for us either

silent renew, periodic check finished! Checking: silentRenewRunning: false id_token: true userData: true Has id_token expired: false, 1617068391000 > 1617025794000 Has access_token expired: true, 1617025791000 > 1617025794000 starting silent renew... BEGIN refresh session Authorize RefreshSession created. adding myautostate: ... found refresh code, obtaining new credentials with refresh code token refresh response: {access_token: "blabla", token_type: "Bearer", expires_in: 599, refresh_token: "blabla2", scope: "scope1 scope2 profile"} history clean up inactive {access_token: "blabla", token_type: "Bearer", expires_in: 599, refresh_token: "blabla2", scope: "scope1 scope2 profile", 鈥 authorizedCallback created, begin token validation Getting signinkeys from https://.../oauth2/api/jwks No id_token found, skipping id_token validation AuthorizedCallback token(s) validated, continue storing the accessToken silent renew, periodic check finished! Checking: silentRenewRunning: false id_token: false userData: true silent renew, periodic check finished! Checking: silentRenewRunning: false id_token: false userData: true silent renew, periodic check finished! Checking: silentRenewRunning: false id_token: false userData: true
So it stops checking the timestamp after
storage:
_authnResult : {"access_token":"blabla","token_type":"Bearer","expires_in":599,"refresh_token":blabla","scope":"scope1 scope2 openid profile","state":"4f3bc84dfc20485ae8c25bdb5f138472f8vU0Bxl8"} _session_state: null _access_token_expires_at: 1617026504000 _codeVerifier: "f48efd7ffd0068db4c848a2133e3573ef4d014e76b847216508ee9ed49525roQkoD" _authStateControl: null _storageSilentRenewRunning : null

Hi @damienbod, do you have a solution?

@FiReBlUe45 , @sive87 not yet, I was planning on looking at this, fixing this next chance I get,, just changed jobs and I'm pretty busy at the moment. It's on my todo list.

I'll track, fix this in this issue:
https://github.com/damienbod/angular-auth-oidc-client/issues/1039

I believe this is the same, correct me if I'm wrong

Greetings Damien

Was this page helpful?
0 / 5 - 0 ratings