token_endpoint_controller.rb 2.41 KB
Newer Older
1 2
# frozen_string_literal: true

3 4 5
module Api
  module OpenidConnect
    class TokenEndpointController < ApplicationController
6 7
      skip_before_action :verify_authenticity_token

8 9 10 11 12
      def create
        req = Rack::Request.new(request.env)
        if req["client_assertion_type"] == "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
          handle_jwt_bearer(req)
        end
13 14
        self.status, headers, self.response_body = Api::OpenidConnect::TokenEndpoint.new.call(request.env)
        headers.each {|name, value| response.headers[name] = value }
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
        nil
      end

      private

      def handle_jwt_bearer(req)
        jwt_string = req["client_assertion"]
        jwt = JSON::JWT.decode jwt_string, :skip_verification
        o_auth_app = Api::OpenidConnect::OAuthApplication.find_by(client_id: jwt["iss"])
        raise Rack::OAuth2::Server::Authorize::BadRequest(:invalid_request) unless o_auth_app
        public_key = fetch_public_key(o_auth_app, jwt)
        JSON::JWT.decode(jwt_string, JSON::JWK.new(public_key).to_key)
        req.update_param("client_id", o_auth_app.client_id)
        req.update_param("client_secret", o_auth_app.client_secret)
      end

      def fetch_public_key(o_auth_app, jwt)
32
        public_key = fetch_public_key_from_json(o_auth_app.jwks, jwt)
33
        if public_key.empty? && o_auth_app.jwks_uri
theworldbright's avatar
theworldbright committed
34
          response = Faraday.get(o_auth_app.jwks_uri)
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
          public_key = fetch_public_key_from_json(response.body, jwt)
        end
        raise Rack::OAuth2::Server::Authorize::BadRequest(:unauthorized_client) if public_key.empty?
        public_key
      end

      def fetch_public_key_from_json(string, jwt)
        json = JSON.parse(string)
        keys = json["keys"]
        public_key = get_key_from_kid(keys, jwt.header["kid"])
        public_key
      end

      def get_key_from_kid(keys, kid)
        keys.each do |key|
          return key if key.has_value?(kid)
        end
      end

54 55
      rescue_from Rack::OAuth2::Server::Authorize::BadRequest,
                  JSON::JWT::InvalidFormat, JSON::JWK::UnknownAlgorithm do |e|
56 57 58 59
        logger.info e.backtrace[0, 10].join("\n")
        render json: {error: :invalid_request, error_description: e.message, status: 400}
      end
      rescue_from JSON::JWT::VerificationFailed do |e|
60
        logger.info e.backtrace[0, 10].join("\n")
61 62 63 64 65
        render json: {error: :invalid_grant, error_description: e.message, status: 400}
      end
    end
  end
end