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/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
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
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