README.md in api_signature-0.1.5 vs README.md in api_signature-1.0.0

- old
+ new

@@ -2,11 +2,11 @@ [![Code Climate](https://codeclimate.com/github/psyipm/api_signature/badges/gpa.svg)](https://codeclimate.com/github/psyipm/api_signature) [![Gem Version](https://badge.fury.io/rb/api_signature.svg)](https://badge.fury.io/rb/api_signature) # ApiSignature -Simple HMAC-SHA1 authentication via headers +Simple HMAC-SHA1 authentication via headers. Impressed by [AWS Requests with Signature Version 4](https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html) This gem will generate signature for the client requests and verify that signature on the server side ## Installation @@ -14,104 +14,95 @@ ```ruby gem 'api_signature' ``` -And then execute: +## Usage - $ bundle +The usage is pretty simple. To sign a request use ApiSignature::Signer and for validation use ApiSignature::Validator. -## Usage +### Create signature -### Server side +Sign a request with 'authorization' header. You can change header name, see Configuration section. -Implement warden strategy: ```ruby -module MyApplication - module API - class ClientAuthenticatable < Warden::Strategies::Base - delegate :valid?, to: :api_request +api_access_key = 'access_key' +api_secret_key = 'secret_key' - def authenticate! - # Find client in database by public api_key - resource = Client.find_for_token_authentication(api_request.access_key) - return fail!(:not_found_in_database) unless resource +request = { + http_method: 'POST', + url: 'https://example.com/posts', + headers: { + 'User-Agent' => 'Test agent' + }, + body: 'body' +} - # Check request signature - return unless api_request.correct?(resource.api_key, resource.api_secret) +# Sign your request +signature = ApiSignature::Signer.new(api_access_key, api_secret_key).sign_request(request) - # Perform some after_authentication callbacks - resource.after_authentication +# Now apply signed headers to your real request +signature.headers - # Tell warden that authentication was successful - success!(resource) - end - - private - - def api_request - @api_request ||= ::ApiSignature::Request.new(env) - end - end - end -end +# signature.headers looks like: +{ + "host"=>"example.com", + "x-datetime"=>"2020-01-02T10:24:59.837+0000", + "authorization"=>"API-HMAC-SHA256 Credential=access_key/20200102/web/api_request, SignedHeaders=host;user-agent;x-datetime, Signature=032fc0b7defd66d86ef43ced8e6c3ee351ede21deca6bf1f89b9145f7a9105c1" +} ``` -```ruby -module MyApplication - module API - module Authentication - extend ActiveSupport::Concern +### Validate signature - protected +Validate the request on the client-side. Note, that access_key can be extracted from the request. - def warden - @warden ||= request.env['warden'] - end - - def current_client - @current_client ||= warden.user(:client) - end - - def authenticate_client! - warden.authenticate!(:client_authenticatable, scope: :client) - end - end - end -end -``` - ```ruby -class Api::BaseController < ActionController::API do - abstract! +# the request to validate +request = { + :http_method=>"POST", + :url=>"https://example.com/posts", + :headers=>{ + "User-Agent"=>"Test agent", + "host"=>"example.com", + "x-datetime"=>"2020-01-02T10:24:59.837+0000", + "authorization"=>"API-HMAC-SHA256 Credential=access_key/20200102/web/api_request, SignedHeaders=host;user-agent;x-datetime, Signature=032fc0b7defd66d86ef43ced8e6c3ee351ede21deca6bf1f89b9145f7a9105c1" + }, + :body=>"body" +} - include MyApplication::API::Authentication +# initialize validator with a request to validate +validator = ApiSignature::Validator.new(request) - before_action :authenticate_client! -end -``` +# get access key from request headers (String) +validator.access_key -### On client side: +# validate the request (Boolean) +validator.valid?('your secret key here') -```ruby -options = { - request_method: 'GET', - path: '/api/v1/some_path' - access_key: 'client public api_key', - timestamp: Time.now.utc.to_i -} - -signature = ApiSignature::Generator.new(options).generate_signature('api_secret') +# get only signed headers (Hash) +validator.signed_headers ``` -By default, the generated signature will be valid for 2 hours +## Configuration + +By default, the generated signature will be valid for 5 minutes This could be changed via initializer: ```ruby # config/initializers/api_signature.rb ApiSignature.setup do |config| - config.signature_ttl = 1.minute + # Time to live, by default 5 minutes + config.signature_ttl = 5 * 60 + + # Datetime format, by default iso8601 + config.datetime_format = '%Y-%m-%dT%H:%M:%S.%L%z' + + # Header name, by default authorization + config.signature_header = 'authorization' + + # Service name, by default web + config.service = 'web' end ``` ## Testing