lib/oauth2/authenticator.rb
# frozen_string_literal: true
require 'base64'
module OAuth2
class Authenticator
attr_reader :mode, :id, :secret
def initialize(id, secret, mode)
@id = id
@secret = secret
@mode = mode
end
# Apply the request credentials used to authenticate to the Authorization Server
#
# Depending on configuration, this might be as request params or as an
# Authorization header.
#
# User-provided params and header take precedence.
#
# @param [Hash] params a Hash of params for the token endpoint
# @return [Hash] params amended with appropriate authentication details
def apply(params)
case mode.to_sym
when :basic_auth
apply_basic_auth(params)
when :request_body
apply_params_auth(params)
when :tls_client_auth
apply_client_id(params)
when :private_key_jwt
params
else
raise NotImplementedError
end
end
def self.encode_basic_auth(user, password)
"Basic #{Base64.strict_encode64("#{user}:#{password}")}"
end
private
# Adds client_id and client_secret request parameters if they are not
# already set.
def apply_params_auth(params)
result = {}
result['client_id'] = id unless id.nil?
result['client_secret'] = secret unless secret.nil?
result.merge(params)
end
# When using schemes that don't require the client_secret to be passed i.e TLS Client Auth,
# we don't want to send the secret
def apply_client_id(params)
result = {}
result['client_id'] = id unless id.nil?
result.merge(params)
end
# Adds an `Authorization` header with Basic Auth credentials if and only if
# it is not already set in the params.
def apply_basic_auth(params)
headers = params.fetch(:headers, {})
headers = basic_auth_header.merge(headers)
params.merge(headers: headers)
end
# @see https://datatracker.ietf.org/doc/html/rfc2617#section-2
def basic_auth_header
{'Authorization' => self.class.encode_basic_auth(id, secret)}
end
end
end