Hi
@Matt Jenkins,
Just to colour around this a little...
What I meant with my initial post is that (with the original `refresh_token`) I can run the code below over and over, generating a new `refresh` and `access` token each time. Each `refresh_token` and `access_token` returned is useable:
fetch("https://example.verify.ibm.com/v1.0/endpoint/default/token", {
"method": "POST",
"headers": {
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json"
},
"body": {
"grant_type": "refresh_token",
"refresh_token": "__ORIGINAL_REFRESH_TOKEN__",
"client_id": "XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX"
}
})
.then(response => {
console.log(response);
})
.catch(err => {
console.error(err);
});
My expectation was that the above request would only work
once because the original `refresh_token` would expire immediately after requesting a new token. That doesn't appear to be the case.
After a bit more testing, I noticed that the
Renew refresh token lifetime option appears to invalidate old tokens once the new one has been used. But, that experience isn't consistent. I've found situations where I've requested a new `access_token` and found that the original and newly-issued `access_token` both remain active. Steps to reproduce would be:
- Go through Authorization Code + PKCE authorization flow.
- Use `refresh_token` from step 1 to generate a new `access_token`/`refresh_token` pair.
- Do API call with `access_token` (from step 1) <-- This works.
- Do API call with newly-issued `access_token` (from step 2) <-- This works too!
- Do API call again with `access_token` (from step 1) <-- This works, still!
For now, I've gotten around this by using very short expiry times. But, it might be something worth looking into.
------------------------------
Timothy
------------------------------
Original Message:
Sent: Mon May 09, 2022 10:44 AM
From: Matt Jenkins
Subject: Rotating Refresh Tokens
@Sylvain Gilbert Thanks! I didn't even realize that was an option. In my case, I just tested and the refresh tokens are in fact not any good after it refreshes. In my lab I do NOT have the option checked (running v10.0.3.1). It is really good to know about this option, as our developers are currently working to implement refresh tokens so I expect weird stuff like this to arise here over the next few months. Many thanks for pointing it out.
Maybe @Timothy Dilbert didn't realize his tokens were actually being revoked as well. I didn't realize this until I specifically tested them (all my test cases were setup to use the most recent refresh token, I had never tried to pass the first one before until now when I altered the test case).
------------------------------
Matt Jenkins
Original Message:
Sent: Mon May 09, 2022 10:13 AM
From: Sylvain Gilbert
Subject: Rotating Refresh Tokens
Hi
Does anyone of you reporting the behavior make use of the feature "Enable multiple refresh tokens for fault tolerance" referred here ?
https://www.ibm.com/docs/en/sva/10.0.1?topic=protection-api-token-management-properties#api_prot_token_mgmt_props
Specifies how refresh tokens are handled. When this option is enabled, and a refresh request is made, the initially-used refresh token remains active (assuming it was initially active), even after a successful refresh request is made and a new token pair (access token and refresh token) is returned. Only upon the subsequent use of the new access token or new refresh token will the initially presented refresh token be invalidated. If the initially used refresh token is presented again, the tokens issued on the first refresh request (Pair 1) are revoked, and another token pair (access token and refresh token) is issued. This new pair (Pair 2) is valid, and Pair 1 is invalid.
This option is available only if you enable the Issue refresh token option.
Default value: disabled
This feature is useful I found in cases where there might be a racing condition inside multi-threaded client apps thus preventing to immediately terminate the previous RT when obtaining a new one. But this feature is disabled by default.
------------------------------
Sylvain Gilbert
Original Message:
Sent: Mon May 09, 2022 08:49 AM
From: Matt Jenkins
Subject: Rotating Refresh Tokens
I meant to say "If it were me, I'd probably just do it in the mapping rules if the refresh token being used for the authorization at the time of the refresh request is available to issue a delete token on from the mapping rule."
Ok the community keeps crashing when I try to edit my post so hopefully a second reply works. :)
------------------------------
Matt Jenkins
Original Message:
Sent: Mon May 09, 2022 08:44 AM
From: Matt Jenkins
Subject: Rotating Refresh Tokens
I was curious about this as we are working with similar flows and I'll likely get asked the same question.
I looked up the spec: https://datatracker.ietf.org/doc/html/rfc6749#section-6
It reads:
The authorization server MAY issue a new refresh token, in which case the client MUST discard the old refresh token and replace it with the new refresh token. The authorization server MAY revoke the old refresh token after issuing a new refresh token to the client. If a new refresh token is issued, the refresh token scope MUST be identical to that of the refresh token included by the client in the request.
I agree with you, it does seem like it should be revoked. If it were me, I'd probably just do it in the mapping rules if the refresh token being used for the authorization at the time of the refresh request, Maybe in the post rule, after the new access and refresh tokens are generated and prepared to be returned.
I'll be curious what anyone else says on this. This conversation may have generated a new backlog item for me, thanks for bringing it up.
------------------------------
Matt Jenkins
Original Message:
Sent: Sat May 07, 2022 10:45 PM
From: Timothy Dilbert
Subject: Rotating Refresh Tokens
We have a SPA web application authenticating using OIDC + PKCE.
I am implementing logic where once the `access_token` expires, I am requesting a new one using the `refresh_token`. When Verify returns with a new `access_token` a new `refresh_token` is also issued. I'm noticing, however, that the old `refresh_token` (i.e. the one I used in the request) is still valid.
Example of an API call requesting a new `access_token`, which at the same time returns a new `refresh_token`:
fetch("https://example.verify.ibm.com/v1.0/endpoint/default/token", { "method": "POST", "headers": { "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json" }, "body": { "grant_type": "refresh_token", "refresh_token": "HXXXXXXXXXXXX30c", "client_id": "XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX" }}).then(response => { console.log(response);}).catch(err => { console.error(err);});
Example of the response received from Verify:
{ "access_token": "XXXXaccess_tokenXXXXXX", "refresh_token": "XXXXresfreshXXXXXX", "scope": "openid profile email", "grant_id": "XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXX", "id_token": "XXXXXXXXX", "token_type": "Bearer", "expires_in": 1399}
My question(s)
My concern is that I now have two `refresh_token`, both of which can be used to request additional `access_token`. My goal is to reduce the blast radius by having only a single active `refresh_token`. Using the latest `refresh_token` returned in the token response feels like the most secure option.
It is the client application's responsibility to revoke the old `refresh_token`? Or, is there a setting within Verify I should select to make the old `refresh_token` expire as soon as it's used to request a new `access_token`?
------------------------------
Timothy
------------------------------