doorkeeper: Token Introspection failed with a protected resource's credential

RFC7622 OAuth 2.0 Token Introspection says,

   To prevent token scanning attacks, the endpoint MUST also require
   some form of authorization to access this endpoint, such as client
   authentication as described in OAuth 2.0 [RFC6749] or a separate
   OAuth 2.0 access token such as the bearer token described in OAuth
   2.0 Bearer Token Usage [RFC6750].

   ...

   In this example, the protected resource uses a client identifier and
   client secret to authenticate itself to the introspection endpoint.

So Token Introspection Endpoint should require a protected resource’s credential. But doorkeeper’s implementation requires a credential of the client the access token was issued to. https://github.com/doorkeeper-gem/doorkeeper/blob/a729e80248031c1030c4dc0f89474932fe0d6603/lib/doorkeeper/oauth/token_introspection.rb#L104-L109

Steps to reproduce

Send a request to Token Introspection Endpoint with a protected resource’s client_id/client_secret.

Expected behavior

Token Introspection Endpoint returns a success response.

Actual behavior

Token Introspection Endpoint returns a failure response.

{ active: false }

System configuration

Doorkeeper initializer: no customize

Ruby version: 2.5.3

Gemfile.lock:

doorkeeper (5.0.2)                                                                                                                                                                                                                                                            
  railties (>= 4.2)

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 1
  • Comments: 15 (8 by maintainers)

Commits related to this issue

Most upvoted comments

I mean if clients want to introspect token-a, they need to specify another token (e.g., token-b) in the Authorizaiton header.

So, this is NG.

POST /introspect
Authorization: Bearer <token-a>

access_token=<token-a>

This is OK

POST /introspect
Authorization: Bearer <token-b>

access_token=<token-a>

Yes, it solves my problem. Thanks!

That is what separate mean in the RFC 7662 Section 2.1. If you want to use bearer tokens for token introspection API access control, you need an access token separated from the introspected token.

To prevent token scanning attacks, the endpoint MUST also require
some form of authorization to access this endpoint, such as client
authentication as described in OAuth 2.0 [RFC6749] or a separate
OAuth 2.0 access token such as the bearer token described in OAuth
2.0 Bearer Token Usage [RFC6750].

ref.) https://tools.ietf.org/html/rfc7662#section-2.1

So Token Introspection Endpoint should require a protected resource’s credential. But doorkeeper’s implementation requires a credential of the client the access token was issued to.

OK @kurodashota-mf , I remember from where it:

https://www.oauth.com/oauth2-servers/token-introspection-endpoint/

see Error Response section. Need to investigate more to fix the issue.

Any other error is considered an “inactive” token.

  • The token requested does not exist or is invalid
  • The token expired
  • The token was issued to a different client than is making this request

So Doorkeeper checks if client credentials used to authenticate is the same as for client that token was issued for. I agree with nov that the second check (if no credentials that just check the token to be valid) is cucumbersome and need to be fixed.

Could you please propose a gist RSpec example that fails with current behavior?

As @kurodashota-mf says, Token Introspection (RFC 7662) is designed for Resource Servers (RS), not for Clients.

This spec enables RS calls Token Introspection API, so that it can detect which client is accessing to its resource with which scopes. It also imply RS has its own client credentials issued from AS.

This spec doesn’t prohibit Clients use the API, so that client can use the API to mitigate token replace attack. However, it’s not original purpose of the RFC. It’s just a side effect.

So, Token Introspection API should accept client credentials not only of OAuth Clients, but of Resource Servers.

ref.) https://tools.ietf.org/html/rfc7662#section-1

Since OAuth 2.0 does not define a protocol for the resource server to learn meta-information about a token that it has received from an authorization server, several different approaches have been developed to bridge this gap. : This specification defines a protocol that allows authorized protected resources to query the authorization server to determine the set of metadata for a given token that was presented to them by an OAuth 2.0 client.

Plus, current doorkeeper seems accept whether

  • client credentials to which the token is issued or
  • any valid access tokens

First one reject any other clients other than the client to which the token is issued, but the second accept any clients who have valid access tokens. It looks weird. If the introspected token is valid, anyone can specify it in bearer header too. Then the call success. I don’t get the design concept…

In general, the API would accept whether

  • Client’s client credentials to which the token is issued or
  • RS’s client credentials at which the token can be used or
  • valid access tokens of above client / RS

ref.) https://tools.ietf.org/html/rfc7662#section-4

For instance, these tests include the following:

  • If the token can expire, the authorization server MUST determine whether or not the token has expired.
  • If the token can be issued before it is able to be used, the authorization server MUST determine whether or not a token’s valid period has started yet.
  • If the token can be revoked after it was issued, the authorization server MUST determine whether or not such a revocation has taken place.
  • If the token has been signed, the authorization server MUST validate the signature.
  • If the token can be used only at certain resource servers, the authorization server MUST determine whether or not the token can be used at the resource server making the introspection call.

Hi @nbulaj. Thank you for your quick response.

There may be a little misunderstanding. I don’t want to the second solution.

Doorkeeper returns failure response in case The token was issued to a different client than is making this request. But I think it’s wrong.

From my understanding, in Token Introspection Endpoint, client_id and client_secret are not for authenticating a client an access_token issued to, but for authenticating a protected resource.

Hi @kurodashota-mf . Thanks for reporting this issue!

Let’s look:

such as client authentication as described in OAuth 2.0 [RFC6749] or a separate OAuth 2.0 access token such as the bearer token
described in OAuth 2.0 Bearer Token Usage [RFC6750].

So Doorkeeper can implement either first or second type of authorization (not both), right?

Btw, I agree that we can implement the second solution from the spec. Would you like to submit a PR?