Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bearer Token Authorization Header To Connection #299

Merged
merged 4 commits into from
Oct 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,28 @@ Active Resource supports the token based authentication provided by Rails throug
You can also set any specific HTTP header using the same way. As mentioned above, headers are thread-safe, so you can set headers dynamically, even in a multi-threaded environment:

ActiveResource::Base.headers['Authorization'] = current_session_api_token

Global Authentication to be used across all subclasses of ActiveResource::Base should be handled using the ActiveResource::Connection class.

ActiveResource::Base.connection.auth_type = :bearer
ActiveResource::Base.connection.bearer_token = @bearer_token

class Person < ActiveResource::Base
self.connection.auth_type = :bearer
self.connection.bearer_token = @bearer_token
end

ActiveResource supports 2 options for HTTP authentication today.

1. Basic

ActiveResource::Connection.new("http://my%40email.com:%31%32%33@localhost")
# username: [email protected] password: 123

2. Bearer Token

ActiveResource::Base.connection.auth_type = :bearer
ActiveResource::Base.connection.bearer_token = @bearer_token

==== Protocol

Expand Down
13 changes: 10 additions & 3 deletions lib/active_resource/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Connection
:head => 'Accept'
}

attr_reader :site, :user, :password, :auth_type, :timeout, :open_timeout, :read_timeout, :proxy, :ssl_options
attr_reader :site, :user, :password, :bearer_token, :auth_type, :timeout, :open_timeout, :read_timeout, :proxy, :ssl_options
attr_accessor :format, :logger

class << self
Expand All @@ -33,7 +33,7 @@ def requests
# attribute to the URI for the remote resource service.
def initialize(site, format = ActiveResource::Formats::JsonFormat, logger: nil)
raise ArgumentError, 'Missing site URI' unless site
@proxy = @user = @password = nil
@proxy = @user = @password = @bearer_token = nil
self.site = site
self.format = format
self.logger = logger
Expand Down Expand Up @@ -62,6 +62,11 @@ def password=(password)
@password = password
end

# Sets the bearer token for remote service.
def bearer_token=(bearer_token)
@bearer_token = bearer_token
end

# Sets the auth type for remote service.
def auth_type=(auth_type)
@auth_type = legitimize_auth_type(auth_type)
Expand Down Expand Up @@ -240,6 +245,8 @@ def authorization_header(http_method, uri)
else
{ 'Authorization' => 'Basic ' + ["#{@user}:#{@password}"].pack('m').delete("\r\n") }
end
elsif @bearer_token
{ 'Authorization' => "Bearer #{@bearer_token}" }
else
{}
end
Expand Down Expand Up @@ -294,7 +301,7 @@ def http_format_header(http_method)
def legitimize_auth_type(auth_type)
return :basic if auth_type.nil?
auth_type = auth_type.to_sym
auth_type.in?([:basic, :digest]) ? auth_type : :basic
auth_type.in?([:basic, :digest, :bearer]) ? auth_type : :basic
end
end
end
22 changes: 22 additions & 0 deletions test/cases/authorization_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ def setup
@david = { :person => { :id => 2, :name => 'David' } }.to_json
@authenticated_conn = ActiveResource::Connection.new("http://david:test123@localhost")
@basic_authorization_request_header = { 'Authorization' => 'Basic ZGF2aWQ6dGVzdDEyMw==' }
@jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
@bearer_token_authorization_request_header = { 'Authorization' => "Bearer #{@jwt}" }
end

private
Expand All @@ -25,6 +27,7 @@ def setup
ActiveResource::HttpMock.respond_to do |mock|
mock.get "/people/2.json", @basic_authorization_request_header, @david
mock.get "/people/1.json", @basic_authorization_request_header, nil, 401, { 'WWW-Authenticate' => 'i_should_be_ignored' }
mock.get "/people/3.json", @bearer_token_authorization_request_header, @david
mock.put "/people/2.json", @basic_authorization_request_header, nil, 204
mock.delete "/people/2.json", @basic_authorization_request_header, nil, 200
mock.post "/people/2/addresses.json", @basic_authorization_request_header, nil, 201, 'Location' => '/people/1/addresses/5'
Expand Down Expand Up @@ -148,6 +151,25 @@ def test_authorization_header_if_credentials_supplied_and_auth_type_is_basic
assert_equal ["david", "test123"], ::Base64.decode64(authorization[1]).split(":")[0..1]
end

def test_authorization_header_explicitly_setting_jwt_and_auth_type_is_bearer
@conn = ActiveResource::Connection.new("http://localhost")
@conn.auth_type = :bearer
@conn.bearer_token = @jwt
authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/3.json'))
assert_equal @bearer_token_authorization_request_header['Authorization'], authorization_header['Authorization']
authorization = authorization_header["Authorization"].to_s.split

assert_equal "Bearer", authorization[0]
assert_equal @jwt, authorization[1]
end

def test_authorization_header_if_no_jwt_and_auth_type_is_bearer
@conn = ActiveResource::Connection.new("http://localhost")
@conn.auth_type = :bearer
authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/3.json'))
assert_nil authorization_header['Authorization']
end

def test_client_nonce_is_not_nil
assert_not_nil ActiveResource::Connection.new("http://david:test123@localhost").send(:client_nonce)
end
Expand Down