doorkeeper: Recent Change in Token Revocation Endpoint Behavior Breaks Revocation of Tokens for Public Client
Steps to reproduce
I have a mobile app using a Rails server, which in turn depends on Doorkeeper to generate access tokens for API calls. I used to be able to logout successfully from the client using a call to Doorkeeper::TokensController.revoke. However, after upgrading to version Doorkeeper 5.4.0, I got HTTP response 401 during logout.
Expected behavior
Token revocation using a valid access token from Resource Owner Password Credentials Flow should work.
Actual behavior
Token revocation fails with a valid access token from Resource Owner Password Credentials Flow.
System configuration
In version 5.3.3, before this change https://github.com/doorkeeper-gem/doorkeeper/pull/1370/commits/d194bc40e335464e67bc36dfd7e2e61247547545, a token granted to a public client (https://tools.ietf.org/html/rfc6749#section-2.1) via Resource Owner Password Credentials Flow works fine as the method Doorkeeper::TokensController.authorized? is used for authentication.
In version 5.4.0, the method Doorkeeper::TokensController.revoke is modified to require a server.client, which is a confidential client (https://tools.ietf.org/html/rfc6749#section-2.1).
Ruby version: 2.6.5
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 30 (21 by maintainers)
You’re saying that all those years I was using doorkeeper with password grant flow and without creating an application… was a bug. That bug is probably the reason a lot of people love doorkeeper. Simplicity and literally zero setup to have email+password authentication in any project. 😄
It’s not an issue actually, and it’s a known breaking change:
Known (at least now) issue is that you guys could use ROPC grant without using a client which would be fixed soon, yep.
I think I can close it for now, just need to remember issue number for the same threads in the near future.
Since there is nothing much I can do except to downgrade to version 5.3.3, I guess you can make this a known issue on the release notes before closing this. That could save other’s time to research on this. Thanks.
Yep, this is one of the options I think about. I just don’t like to bloat the configuration with a lot of options, especially when there is no need in them.
So I think I’ll add such option, but with deprecation and it will be removed in some major update.
It’s not exactly about it being hard. It’s pointless when it’s for a front-end app that’s all in a browser. Right now I can have two dockerized projects that require running
docker-compose upand everything works out of the box. UI communicates with the API and everything’s great. I don’t have to go into oauth applications endpoint, then create an app, then update env var so the UI knows client_id (or I don’t have to provide client_id at build time so compiled code has it) and so on.Oh guys, I think I have bad news for you (and everyone else).
I think we must fix this too and break a lot of clients that creates tokens without any client authorization 😦 I foresee much more such issues in a near future…
We have
confidentialcolumn that indicates if application is public/private and always generate an ID (and secret in case of private client). So we must always require authorization for this grant flowPublic client means it doesn’t have s secret, just ID. Public client means that client exists and it could be identified just by ID. Please check also section 5. Security Considerations of RFC7009:
Sorry, but I wouldn’t support this “May. Doesn’t have to” for the security reasons. Nothing actually stops you from creating a public client and using it’s credentials to authorize the request.
Nope. Lack of authentication just open a security hole for brute-forcing (throtling) any tokens you have.
Okay, I’ve taken a look.
RFC7009#2.1 says that
tokenis required and authentication must be included as stated in RFC6749#2.3.RFC7009#2.1 also provides an example that is considered valid:
So Basic Authorization header would be the client_id + client_secret if I understand correctly.
But there’s RFC6749#2.3. First paragraph - only for confidential clients. Second paragraph - continuation of that. Third paragraph:
May. Doesn’t have to. So shouldn’t this have worked when testing:
It has
tokenwhich is required. That token has no application (client). Wouldn’t lack of application impact the flow somehow? Before the changes in latest version, token was derived fromheaders: { 'Authorization': "Bearer #{access_token.token}" }implicitly somehow.I feel like I’m still getting this wrong or missing some crucial part here.
Hi @edmondchui .
Did you do this from your mobile app?
server.clientwhich you mentioned just stores client instance that performs a request. It is taken from the credentials used to authorize the request. And if it’s blank - aka no auth passed or wrong credentials used - request is blocked as stated in the the RFC.You need to call POST /token/revoke with
?token=value of access token you wanna to revoke and include authorization headers (HTTP-Authrozation: Basic Base64(client_id:client_secret)) to inform the server that this request is authorized to perform an action. Alternatively you could send client_id and client_secret via body params, but this method isn’t recommended by the RFC. Don’t forget that if the token you wanna to revoke was issued to some specific client - only this client could revoke the token (it’s credentials must be used to authorize the request).More details: section 2.1. Revocation Request of the RFC7009 and then section 2.3. Client Authentication of RFC6749