I'm using Doorkeeper 5.2.1 and Ive consulted the Doorkeeper docs on refresh tokens and have read through several GitHub issues and pull requests related to refresh tokens, notably here and here.
From what I gather from these conversations and from reading the spec (and documents and posts referencing the spec, the following statements are true:
- Refresh tokens are long lived (as opposed to access tokens, which typically expire after a relatively short TTL)
- An authorization server MAY implement revocation
- Doorkeeper implements refresh token revocation on some basis (though I'm not clear what, keep reading)
I'm confused, though, based on this pull request, which implements refresh token expiry after an "access token created with that refresh token is successfully used once." How would Doorkeeper know if I've made an API request with that access token successfully? It's my API that works out the authorization logic based on that access token. Doorkeeper is agnostic to my API requests being successful or not. However, I've interpreted that to mean that if the resource_owner_authenticator
block returns a user, that could constitute successful use. Yet I have not found that that expires a refresh token successfully. (See my spec below.)
It also appears from reading this spec file that if you successfully use a refresh token, it revokes the previous refresh tokens, which would make sense.
I'm trying to work all this out in my spec file, though, and I'm running into an issue where it doesn't appear that a refresh token is revoked, even when it's used multiple times, or it ("refresh token A") is reused after the refresh token ("token B") that returns with the access token that refresh token A is used to generate, is also used. My spec file will make this clearer:
describe 'OAuth flow' do
# ...
describe 'refresh tokens' do
# ...
context 'when attempting reuse of a refresh token' do
before do
redirect_uri = 'https://localhost:3002'
# ivars necessary brecause plaintext tokens/secrets are only available upon creation of the object
@client = Doorkeeper::Application.create(name: 'foo', uid: 'bar', redirect_uri: redirect_uri, scopes: 'project_index')
@secret = @client.plaintext_secret
@grant = Doorkeeper::AccessGrant.create(resource_owner_id: user.id, organization_id: org.id, application_id: @client.id, scopes: 'project_index', expires_in: 600, redirect_uri: redirect_uri)
@code = @grant.plaintext_token
end
it 'revokes the initial refresh token' do
# Get an initial access token and refresh token
post "/oauth/token?client_id=#{@client.uid}&client_secret=#{@secret}&code=#{@code}&grant_type=authorization_code&redirect_uri=#{@client.redirect_uri}&scope=project_index"
expect(response.status).to eq(200)
# Use refresh token to get a new access token
previous_refresh_token = JSON.parse(response.body)['refresh_token']
post "/oauth/token?client_id=#{@client.uid}&client_secret=#{@secret}&code=#{@code}&grant_type=refresh_token&refresh_token=#{previous_refresh_token}&redirect_uri=#{@client.redirect_uri}&scope=project_index"
expect(response.status).to eq(200)
json_body = JSON.parse(response.body)
new_access_token = json_body['access_token']
new_refresh_token = json_body['refresh_token']
# Use the new access token successfully
get "/api/v1/projects?access_token=#{new_access_token}"
expect(response.status).to eq(200)
# Use the new refresh token to get yet another access token
post "/oauth/token?client_id=#{@client.uid}&client_secret=#{@secret}&code=#{@code}&grant_type=refresh_token&refresh_token=#{new_refresh_token}&redirect_uri=#{@client.redirect_uri}&scope=project_index"
expect(response.status).to eq(200)
# Attempt reuse of the first refresh token
post "/oauth/token?client_id=#{@client.uid}&client_secret=#{@secret}&code=#{@code}&grant_type=refresh_token&refresh_token=#{previous_refresh_token}&redirect_uri=#{@client.redirect_uri}&scope=project_index"
expect(response.status).to eq(400)
# ^^^ fails, response is 200 and a new access token and refresh token are generated
end
end
end
# ...
end
This spec fails on the last assertion, suggesting that for a given refresh token, it is not revoked when the access token it is used to generate is used successfully (contrary to what's stated above), or when the refresh token issued with the refreshed access token is used.
My question is, with Doorkeeper, under what circumstances is a refresh token revoked?