module ApiAuth
The gem will sign your requests on the client side and authenticate that signature on the server side. If your server resources are implemented as a Rails
ActiveResource, it will integrate with that. It will even generate the secret keys necessary for your clients to sign their requests.
Public Class Methods
Returns the access id from the request’s authorization header
# File lib/api_auth/base.rb, line 41 def access_id(request) headers = Headers.new(request) if match_data = parse_auth_header(headers.authorization_header) return match_data[1] end nil end
Determines if the request is authentic given the request and the client’s secret key. Returns true if the request is authentic and false otherwise.
# File lib/api_auth/base.rb, line 34 def authentic?(request, secret_key) return false if secret_key.nil? return !md5_mismatch?(request) && signatures_match?(request, secret_key) && !request_too_old?(request) end
Generates a Base64 encoded, randomized secret key
Store this key along with the access key that will be used for authenticating the client
# File lib/api_auth/base.rb, line 54 def generate_secret_key random_bytes = OpenSSL::Random.random_bytes(512) b64_encode(Digest::SHA2.new(512).digest(random_bytes)) end
Signs an HTTP request using the client’s access id and secret key. Returns the HTTP request object with the modified headers.
request: The request can be a Net::HTTP, ActionDispatch::Request, Curb (Curl::Easy) or a RestClient
object.
access_id
: The public unique identifier for the client
secret_key: assigned secret key that is known to both parties
# File lib/api_auth/base.rb, line 25 def sign!(request, access_id, secret_key) headers = Headers.new(request) headers.calculate_md5 headers.set_date headers.sign_header auth_header(request, access_id, secret_key) end
Private Class Methods
# File lib/api_auth/base.rb, line 95 def auth_header(request, access_id, secret_key) "APIAuth #{access_id}:#{hmac_signature(request, secret_key)}" end
# File lib/api_auth/base.rb, line 88 def hmac_signature(request, secret_key) headers = Headers.new(request) canonical_string = headers.canonical_string digest = OpenSSL::Digest.new('sha1') b64_encode(OpenSSL::HMAC.digest(digest, secret_key, canonical_string)) end
# File lib/api_auth/base.rb, line 74 def md5_mismatch?(request) headers = Headers.new(request) headers.md5_mismatch? end
# File lib/api_auth/base.rb, line 99 def parse_auth_header(auth_header) Regexp.new("APIAuth ([^:]+):(.+)$").match(auth_header) end
# File lib/api_auth/base.rb, line 61 def request_too_old?(request) headers = Headers.new(request) # 900 seconds is 15 minutes begin if Time.parse(headers.timestamp).utc < (Time.now.utc - 900) then raise RequestTooOld, "request is more than 900 seconds old" end return false rescue ArgumentError return true end end
# File lib/api_auth/base.rb, line 79 def signatures_match?(request, secret_key) headers = Headers.new(request) if match_data = parse_auth_header(headers.authorization_header) hmac = match_data[2] return hmac == hmac_signature(request, secret_key) end false end