OpenIDConnect.debug!
OpenIDConnect.logger = Rails.logger
Rack::OAuth2.logger = Rails.logger
# Webfinger.logger = Rails.logger
SWD.logger = Rails.logger

# the openid_connect gem does not support
# jwt format in the userinfo call.
# A PR is open to improve the situation
# https://github.com/nov/openid_connect/pull/54
module OpenIDConnect
  class AccessToken < Rack::OAuth2::AccessToken::Bearer
    private

    def jwk_loader
      JSON.parse(URI.parse(ENV['AGENT_CONNECT_JWKS']).read).deep_symbolize_keys
    end

    def decode_jwt(requested_host, jwt)
      agent_connect_host = URI.parse(ENV['AGENT_CONNECT_BASE_URL']).host

      if requested_host == agent_connect_host
        # rubocop:disable Lint/UselessAssignment
        JWT.decode(jwt, key = nil, verify = true, { algorithms: ['ES256'], jwks: jwk_loader })[0]
        # rubocop:enable Lint/UselessAssignment
      else
        raise "unknwon host : #{requested_host}"
      end
    end

    def resource_request
      res = yield
      case res.status
      when 200
        hash = case parse_type_and_subtype(res.content_type)
        when 'application/jwt'
          requested_host = URI.parse(client.userinfo_endpoint).host
          decode_jwt(requested_host, res.body)
        when 'application/json'
          JSON.parse(res.body)
        end
        hash&.with_indifferent_access
      when 400
        raise BadRequest.new('API Access Faild', res)
      when 401
        raise Unauthorized.new('Access Token Invalid or Expired', res)
      when 403
        raise Forbidden.new('Insufficient Scope', res)
      else
        raise HttpError.new(res.status, 'Unknown HttpError', res)
      end
    end

    # https://datatracker.ietf.org/doc/html/rfc2045#section-5.1
    # - type and subtype are the first member
    # they are case insensitive
    def parse_type_and_subtype(content_type)
      content_type.split(';')[0].strip.downcase
    end
  end
end