main
  1# frozen_string_literal: true
  2
  3module Oauth
  4  class TokensController < ActionController::API
  5    include ActionController::HttpAuthentication::Basic::ControllerMethods
  6    before_action :authenticate!
  7
  8    def create
  9      response.headers['Cache-Control'] = 'no-store'
 10      response.headers['Pragma'] = 'no-cache'
 11
 12      @access_token, @refresh_token = tokens_for(params[:grant_type])
 13      return bad_request if @access_token.nil?
 14
 15      render formats: :json
 16    rescue StandardError => error
 17      Rails.logger.error(error)
 18      bad_request
 19    end
 20
 21    def introspect
 22      claims = Token.claims_for(params[:token], token_type: :any)
 23      if claims.empty? || Token.revoked?(claims[:jti])
 24        render json: { active: false }, status: :ok
 25      else
 26        render json: claims.merge(active: true), status: :ok
 27      end
 28    end
 29
 30    def revoke
 31      claims = Token.claims_for(params[:token], token_type: :any)
 32      current_client.revoke(Token.find(claims[:jti])) unless claims.empty?
 33      render plain: "", status: :ok
 34    rescue StandardError => error
 35      logger.error(error)
 36      render plain: "", status: :ok
 37    end
 38
 39    private
 40
 41    attr_reader :current_client
 42
 43    def authenticate!
 44      @current_client = authenticate_with_http_basic do |id, client_secret|
 45        Client.find(id)&.authenticate(client_secret)
 46      end
 47      return if current_client
 48
 49      render "invalid_client", formats: :json, status: :unauthorized
 50    end
 51
 52    def bad_request
 53      render "bad_request", formats: :json, status: :bad_request
 54    end
 55
 56    def authorization_code_grant(code, verifier)
 57      authorization = current_client.authorizations.active.find_by!(code: code)
 58      return unless authorization.valid_verifier?(verifier)
 59
 60      authorization.issue_tokens_to(current_client)
 61    end
 62
 63    def refresh_grant(refresh_token)
 64      jti = Token.claims_for(refresh_token, token_type: :refresh)[:jti]
 65      token = Token.find(jti)
 66      token.issue_tokens_to(current_client)
 67    end
 68
 69    def password_grant(username, password)
 70      user = User.login(username, password)
 71      user.issue_tokens_to(current_client)
 72    end
 73
 74    def saml_assertion_grant(raw)
 75      assertion = Saml::Kit::Assertion.new(
 76        Base64.urlsafe_decode64(raw)
 77      )
 78      return if assertion.invalid?
 79
 80      user = if assertion.name_id_format == Saml::Kit::Namespaces::PERSISTENT
 81               User.find(assertion.name_id)
 82             else
 83               User.find_by!(email: assertion.name_id)
 84             end
 85      user.issue_tokens_to(current_client)
 86    end
 87
 88    def tokens_for(grant_type = params[:grant_type])
 89      case grant_type
 90      when 'authorization_code'
 91        authorization_code_grant(params[:code], params[:code_verifier])
 92      when 'refresh_token'
 93        refresh_grant(params[:refresh_token])
 94      when 'client_credentials'
 95        [current_client.access_token, nil]
 96      when 'password'
 97        password_grant(params[:username], params[:password])
 98      when 'urn:ietf:params:oauth:grant-type:saml2-bearer' # RFC7522
 99        saml_assertion_grant(params[:assertion])
100        # when 'urn:ietf:params:oauth:grant-type:jwt-bearer' # RFC7523
101        # raise NotImplementedError
102      end
103    end
104  end
105end