lib/api_auth/base.rb in api-auth-1.0.0 vs lib/api_auth/base.rb in api-auth-1.0.1
- old
+ new
@@ -1,81 +1,97 @@
# api-auth is Ruby gem designed to be used both in your client and server
-# HTTP-based applications. It implements the same authentication methods (HMAC)
+# HTTP-based applications. It implements the same authentication methods (HMAC)
# used by Amazon Web Services.
-# 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
+# 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.
module ApiAuth
-
+
class << self
-
+
include Helpers
-
+
# 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, ActionController::Request,
+ # request: The request can be a Net::HTTP, ActionController::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
+ # secret_key: assigned secret key that is known to both parties
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
-
+
# 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.
def authentic?(request, secret_key)
return false if secret_key.nil?
-
- 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
+
+ return !md5_mismatch?(request) && signatures_match?(request, secret_key) && !request_too_old?(request)
end
-
+
# Returns the access id from the request's authorization header
def access_id(request)
headers = Headers.new(request)
if match_data = parse_auth_header(headers.authorization_header)
return match_data[1]
end
-
+
nil
end
-
+
# Generates a Base64 encoded, randomized secret key
#
- # Store this key along with the access key that will be used for
+ # Store this key along with the access key that will be used for
# authenticating the client
def generate_secret_key
random_bytes = OpenSSL::Random.random_bytes(512)
b64_encode(Digest::SHA2.new(512).digest(random_bytes))
end
-
+
private
-
+
+ def request_too_old?(request)
+ headers = Headers.new(request)
+ # 900 seconds is 15 minutes
+ Time.parse(headers.timestamp).utc < (Time.current.utc - 900)
+ end
+
+ def md5_mismatch?(request)
+ headers = Headers.new(request)
+ headers.md5_mismatch?
+ end
+
+ 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
+
def hmac_signature(request, secret_key)
headers = Headers.new(request)
canonical_string = headers.canonical_string
digest = OpenSSL::Digest::Digest.new('sha1')
b64_encode(OpenSSL::HMAC.digest(digest, secret_key, canonical_string))
end
-
+
def auth_header(request, access_id, secret_key)
- "APIAuth #{access_id}:#{hmac_signature(request, secret_key)}"
+ "APIAuth #{access_id}:#{hmac_signature(request, secret_key)}"
end
-
+
def parse_auth_header(auth_header)
Regexp.new("APIAuth ([^:]+):(.+)$").match(auth_header)
end
-
+
end # class methods
-
+
end # ApiAuth