class Doorkeeper::TokensController

def after_successful_authorization(context)

def after_successful_authorization(context)
  Doorkeeper.config.after_successful_authorization.call(self, context)
end

def authorize_response

def authorize_response
  @authorize_response ||= begin
    before_successful_authorization
    auth = strategy.authorize
    context = build_context(auth: auth)
    after_successful_authorization(context) unless auth.is_a?(Doorkeeper::OAuth::ErrorResponse)
    auth
  end
end

def authorized?

https://datatracker.ietf.org/doc/html/rfc7009
https://datatracker.ietf.org/doc/html/rfc6749#section-2.1

verified).
types, they set the application_id as null (since the claim cannot be
clients authenticate the resource owner via "password" or "implicit" grant
OAuth client associated with a given access or refresh token. Since public
Doorkeeper determines the client type implicitly via the presence of the

cannot revoke another's tokens.
revoke the provided access or refresh token. This ensures one client
Once a confidential client is authenticated, it must be authorized to

revoked must also belong to the requesting client.
credentials, in the case of a confidential client. The token being
valid client_id, in the case of a public client, or valid client
According to this specification, a client's request must contain a
by making revocation requests against potential token strings.
A malicious client may attempt to guess valid tokens on this endpoint
Section 5. Security Considerations
RFC7009

OAuth 2.0 Section 2.1 defines two client types, "public" & "confidential".
def authorized?
  # Token belongs to specific client, so we need to check if
  # authenticated client could access it.
  if token.application_id? && token.application.confidential?
    # We authorize client by checking token's application
    server.client && server.client.application == token.application
  else
    # Token was issued without client, authorization unnecessary
    true
  end
end

def before_successful_authorization(context = nil)

def before_successful_authorization(context = nil)
  Doorkeeper.config.before_successful_authorization.call(self, context)
end

def build_context(**attributes)

def build_context(**attributes)
  Doorkeeper::OAuth::Hooks::Context.new(**attributes)
end

def create

def create
  headers.merge!(authorize_response.headers)
  render json: authorize_response.body,
         status: authorize_response.status
rescue Errors::DoorkeeperError => e
  handle_token_exception(e)
end

def introspect

OAuth 2.0 Token Introspection - https://datatracker.ietf.org/doc/html/rfc7662
def introspect
  introspection = OAuth::TokenIntrospection.new(server, token)
  if introspection.authorized?
    render json: introspection.to_json, status: 200
  else
    error = introspection.error_response
    headers.merge!(error.headers)
    render json: error.body, status: error.status
  end
end

def revocation_error_response

def revocation_error_response
  error_description = I18n.t(:unauthorized, scope: %i[doorkeeper errors messages revoke])
  { error: :unauthorized_client, error_description: error_description }
end

def revoke

OAuth 2.0 Token Revocation - https://datatracker.ietf.org/doc/html/rfc7009
def revoke
  # The authorization server responds with HTTP status code 200 if the client
  # submitted an invalid token or the token has been revoked successfully.
  if token.blank?
    render json: {}, status: 200
  # The authorization server validates [...] and whether the token
  # was issued to the client making the revocation request. If this
  # validation fails, the request is refused and the client is informed
  # of the error by the authorization server as described below.
  elsif authorized?
    revoke_token
    render json: {}, status: 200
  else
    render json: revocation_error_response, status: :forbidden
  end
end

def revoke_token

def revoke_token
  # The authorization server responds with HTTP status code 200 if the token
  # has been revoked successfully or if the client submitted an invalid
  # token
  token.revoke if token&.accessible?
end

def strategy

def strategy
  @strategy ||= server.token_request(params[:grant_type])
end

def token

def token
  @token ||=
    if params[:token_type_hint] == "refresh_token"
      Doorkeeper.config.access_token_model.by_refresh_token(params["token"])
    else
      Doorkeeper.config.access_token_model.by_token(params["token"]) ||
        Doorkeeper.config.access_token_model.by_refresh_token(params["token"])
    end
end

def validate_presence_of_client

def validate_presence_of_client
  return if Doorkeeper.config.skip_client_authentication_for_password_grant
  # @see 2.1.  Revocation Request
  #
  #  The client constructs the request by including the following
  #  parameters using the "application/x-www-form-urlencoded" format in
  #  the HTTP request entity-body:
  #     token   REQUIRED.
  #     token_type_hint  OPTIONAL.
  #
  #  The client also includes its authentication credentials as described
  #  in Section 2.3. of [RFC6749].
  #
  #  The authorization server first validates the client credentials (in
  #  case of a confidential client) and then verifies whether the token
  #  was issued to the client making the revocation request.
  return if server.client
  # If this validation [client credentials / token ownership] fails, the request is
  # refused and the  client is informed of the error by the authorization server as
  # described below.
  #
  # @see 2.2.1.  Error Response
  #
  # The error presentation conforms to the definition in Section 5.2 of [RFC6749].
  render json: revocation_error_response, status: :forbidden
end