My app has an access token (4 hour expiry) and refresh token (1 year expiry). Once the access token expires I see it is removed from the oauth_access_tokens table. My refresh token is still in the oauth_refresh_tokens table since it hasn't expired (and revoked=0).
When my app makes the refresh_token call to oauth/token, instead of getting a new access token and refresh token, it gets:
{
"error": "invalid_request",
"message": "The refresh token is invalid.",
"hint": "Token has been revoked"
}
After this response, I can still see my refresh token is in the oauth_refresh_tokens table with revoked=0 and the expiry date is still about a year out.
The RFC seems to indicate refresh tokens are the way to go since they can be used to get a new access token after a previous access token expires, but that isn't the behavior I'm seeing.
As a sanity check, I have verified that I can use the refresh token to get a new access and refresh token pair up until the point that the access token is pruned. If the access token is expired but not yet pruned it still works. It goes sideways once it's been pruned.
Is this the expected behavior? If so, is there something I can do to use a refresh token after the access token has expired & been pruned?
Afaik I think you should indeed still be able to use the refresh token even though the access token is pruned. Maybe @sephster knows what the correct behavior is?
Hey @driesvints. I think it is fine to use the refresh token, even if the access token no longer exists. We actually store the original access token entity in the refresh token so we can compare scopes etc so there shouldn't be a need to keep it (if I am remembering correctly).
The problem seems to be the function isRefreshTokenRevoked in the RefreshTokenRepository which is also checking if the access token is revoked. I don't think this is the correct behaviour.
Okay. I'll have a look at this soonish. Thanks for helping out @Sephster! :)
Sent in a PR for this: https://github.com/laravel/passport/pull/927
@driesvints I was the person that changed this in #391 as part of another fix (deleted entries are valid for ever). Please consider the following:
The GET /oauth/tokens route lists the “approved applications” for a user with DELETE /oauth/tokens/{token_id} revoking a token.
The AuthorizedAccessTokenController::forUser method only lists non-revoked tokens.
Now the user revokes this token, but this does not revoke the refresh token. The application will disappear from the list and the user rightly assumes that it now longer has access. But the application can create itself a new token by using the refresh token.
I see two solutions to this:
AuthorizedAccessTokenController::destroy also revoke the refresh token (and ideally _any other_ token of the same application). This way the user can revoke the token including the refresh token while the application can opt to revoke single tokens, without necessarily killing the refresh token. Show the token in the list when either the normal token or it's refresh token is non-revoked (the user needs to be able to revoke the refresh token even when the normal token is already dead)./oauth/tokens and support revoking them with the /oauth/tokens/{token_id}.Should I create a new issue for this?
@Sephster is it wanted that we also revoke the refresh token when refreshing the access token or add the ability to revoke the refresh token?
It is up to you. Refresh tokens never expire so the decision of when to revoke it is largely undertaken by the authorisation server. There are a number of options you could employ:
The core OAuth 2 spec doesn't allow clients to revoke access/refresh tokens but there is an extension RFC that allows us to do this. We have not implemented this at present.
I would probably recommend that you add manual revocation and revoke after the refresh token has been used. It might be worth building in a slight delay for this in case there are any network issues such as successfully processing the refresh token request but not successfully issuing a new access token. Cheers
@Sephster thanks. Isn't revoking the old token something the OAuth2 server package should be doing instead of us? If you haven't implemented that I don't think it's wise for us to do so.
@TimWolla Manually revocation is already possible with the revokeRefreshToken method on the RefreshTokenRepository repository.
Sorry @driesvints, yes we are. I just checked the code and we do it when we respond to an access token request:
// Expire old tokens
$this->accessTokenRepository->revokeAccessToken($oldRefreshToken['access_token_id']);
$this->refreshTokenRepository->revokeRefreshToken($oldRefreshToken['refresh_token_id']);
@Sephster thanks for checking :)
@TimWolla Manually revocation is already possible with the
revokeRefreshTokenmethod on theRefreshTokenRepositoryrepository.
@driesvints Sorry I've been busy the last days. I understand that I am able to revoke the refresh token within my own code. But I believe it is a bug in Passport that upon explicit user revocal of the access token in AuthorizedAccessTokenController::destroy the refresh token is not being revoked as well [1]. Do you disagree here?
[1] I'm going with resolve(Connection::class)->table('oauth_refresh_tokens')->where('access_token_id', $tokenId)->update(['revoked' => true]); as the easiest solution for my application.
@TimWolla at the moment I'd go with the current implementation as @Sephster explained above that there isn't a general rule for this.
I am facing the same as @TimWolla discovered.
I insist that it is a Passport concern. When the user clicks revoke on the Authorized Clients, it is not revoking the access_token it is revoking the client (and its application) which should result in: 1. The access_token for that user issued by that client is not valid anymore AND 2. Of course, as the user revoked the client application, there should not exist any way to obtain an acccess_token again, which implies the refresh_token should get invalid as well.
Normally, any application will try to use a refresh token when lost access with a regular access_token and, if the user revoked that application, that application should not be able to obtain a new one again, without passing through the user permission or credentials again (code or password grants).
Imagine using Facebook to login to InsecureSite. dot com, then you realize it is insecure so you revoke access, but magically InsecureSite gains access again after a few minutes just by refreshing the token and you do not even realize that!
This is an important concern in fact.
I hope I was clear to illustrate the current issue.
PS: In Passport, you can make a refresh_token to expires, of course, usually much time later than the access_token.
I'll ping @driesvints just in case as I think it is important matter.
I'll re-open this to take another look at a later time. I feel that this is indeed something that needs further attention.
@driesvints
Any update on this?
This is an ideal case for the token revocation RFC. The problem with the original OAuth2 RFC is that it described lots of different ways to obtain an access token but never how to revoke one. It was kind of left up to the implementer.
This effectively meant that you could login to a system but you couldn't reliably log out. The token revocation RFC resolves this. It would provide an end point that allows you to revoke a refresh token or access token. When you revoke a refresh token, it should also revoke all associated access tokens.
We have an open issue and pull request to look at implementing this RFC in League's OAuth2 server. I believe this is probably the best route to go down to resolve your issues.
When are you planning on merging the PR and resolving this issue?
Regards,
Greg
On 18 Jul 2019, at 7:13 pm, Andrew Millington notifications@github.com wrote:
This is an ideal case for the token revocation RFC. The problem with the original OAuth2 RFC is that it described lots of different ways to obtain an access token but never how to revoke one. It was kind of left up to the implementer.
This effectively meant that you could login to a system but you couldn't reliably log out. The token revocation RFC resolves this. It would provide an end point that allows you to revoke a refresh token or access token. When you revoke a refresh token, it should also revoke all associated access tokens.
We have an open issue and pull request to look at implementing this RFC in League's OAuth2 server. I believe this is probably the best route to go down to resolve your issues.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.
I can't say at the moment. As I do this in my spare time, it really depends on how much work/life stuff I have coming up. I will prioritise this though now version 8 is out.
Thanks Andrew for your attention. Really appreciate your response. Look forward to any updates. All the best to you and your teams and families.
Regards,
Greg
On 18 Jul 2019, at 7:13 pm, Andrew Millington notifications@github.com wrote:
This is an ideal case for the token revocation RFC. The problem with the original OAuth2 RFC is that it described lots of different ways to obtain an access token but never how to revoke one. It was kind of left up to the implementer.
This effectively meant that you could login to a system but you couldn't reliably log out. The token revocation RFC resolves this. It would provide an end point that allows you to revoke a refresh token or access token. When you revoke a refresh token, it should also revoke all associated access tokens.
We have an open issue and pull request to look at implementing this RFC in League's OAuth2 server. I believe this is probably the best route to go down to resolve your issues.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.
Most helpful comment
I'll re-open this to take another look at a later time. I feel that this is indeed something that needs further attention.