Skip to content

Commit

Permalink
Drop deprecated auth helpers from Connection. (lostisland#1308)
Browse files Browse the repository at this point in the history
Merge Authentication middleware.
Add possibility of passing a proc.
Update documentation.

(cherry picked from commit fe600c7)
  • Loading branch information
iMacTia authored and jarl-dk committed Sep 9, 2021
1 parent a84e6b4 commit 3fee015
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 220 deletions.
21 changes: 7 additions & 14 deletions docs/middleware/request/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,30 @@ top_link: ./list
---

The `Faraday::Request::Authorization` middleware allows you to automatically add an `Authorization` header
to your requests. It also features 2 specialised sub-classes that provide useful extra features for Basic Authentication
and Token Authentication requests.

### Any Authentication

The generic `Authorization` middleware allows you to add any type of Authorization header.
to your requests. It also features a handy helper to manage Basic authentication.

```ruby
Faraday.new(...) do |conn|
conn.request :authorization, 'Bearer', 'authentication-token'
end
```

### Basic Authentication
### With a proc

`BasicAuthentication` adds a 'Basic' type Authorization header to a Faraday request.
You can also provide a proc, which will be evaluated on each request:

```ruby
Faraday.new(...) do |conn|
conn.request :basic_auth, 'username', 'password'
conn.request :authorization, 'Bearer', -> { MyAuthStorage.get_auth_token }
end
```

### Token Authentication
### Basic Authentication

`TokenAuthentication` adds a 'Token' type Authorization header to a Faraday request.
You can optionally provide a hash of `options` that will be appended to the token.
This is not used anymore in modern web and have been replaced by Bearer tokens.
The middleware will automatically Base64 encode your Basic username and password:

```ruby
Faraday.new(...) do |conn|
conn.request :token_auth, 'authentication-token', **options
conn.request :authorization, :basic, 'username', 'password'
end
```
79 changes: 0 additions & 79 deletions lib/faraday/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -283,77 +283,6 @@ def #{method}(url = nil, body = nil, headers = nil, &block)
RUBY
end

# Sets up the Authorization header with these credentials, encoded
# with base64.
#
# @param login [String] The authentication login.
# @param pass [String] The authentication password.
#
# @example
#
# conn.basic_auth 'Aladdin', 'open sesame'
# conn.headers['Authorization']
# # => "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
#
# @return [void]
def basic_auth(login, pass)
warn <<~TEXT
WARNING: `Faraday::Connection#basic_auth` is deprecated; it will be removed in version 2.0.
While initializing your connection, use `#request(:basic_auth, ...)` instead.
See https://lostisland.github.io/faraday/middleware/authentication for more usage info.
TEXT
set_authorization_header(:basic_auth, login, pass)
end

# Sets up the Authorization header with the given token.
#
# @param token [String]
# @param options [Hash] extra token options.
#
# @example
#
# conn.token_auth 'abcdef', foo: 'bar'
# conn.headers['Authorization']
# # => "Token token=\"abcdef\",
# foo=\"bar\""
#
# @return [void]
def token_auth(token, options = nil)
warn <<~TEXT
WARNING: `Faraday::Connection#token_auth` is deprecated; it will be removed in version 2.0.
While initializing your connection, use `#request(:token_auth, ...)` instead.
See https://lostisland.github.io/faraday/middleware/authentication for more usage info.
TEXT
set_authorization_header(:token_auth, token, options)
end

# Sets up a custom Authorization header.
#
# @param type [String] authorization type
# @param token [String, Hash] token. A String value is taken literally, and
# a Hash is encoded into comma-separated key/value pairs.
#
# @example
#
# conn.authorization :Bearer, 'mF_9.B5f-4.1JqM'
# conn.headers['Authorization']
# # => "Bearer mF_9.B5f-4.1JqM"
#
# conn.authorization :Token, token: 'abcdef', foo: 'bar'
# conn.headers['Authorization']
# # => "Token token=\"abcdef\",
# foo=\"bar\""
#
# @return [void]
def authorization(type, token)
warn <<~TEXT
WARNING: `Faraday::Connection#authorization` is deprecated; it will be removed in version 2.0.
While initializing your connection, use `#request(:authorization, ...)` instead.
See https://lostisland.github.io/faraday/middleware/authentication for more usage info.
TEXT
set_authorization_header(:authorization, type, token)
end

# Check if the adapter is parallel-capable.
#
# @yield if the adapter isn't parallel-capable, or if no adapter is set yet.
Expand Down Expand Up @@ -579,14 +508,6 @@ def with_uri_credentials(uri)
yield(Utils.unescape(uri.user), Utils.unescape(uri.password))
end

def set_authorization_header(header_type, *args)
header = Faraday::Request
.lookup_middleware(header_type)
.header(*args)

headers[Faraday::Request::Authorization::KEY] = header
end

def proxy_from_env(url)
return if Faraday.ignore_env_proxy

Expand Down
71 changes: 36 additions & 35 deletions lib/faraday/request/authorization.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require 'base64'

module Faraday
class Request
# Request middleware for the Authorization HTTP header
Expand All @@ -8,47 +10,46 @@ class Authorization < Faraday::Middleware
KEY = 'Authorization'
end

# @param type [String, Symbol]
# @param token [String, Symbol, Hash]
# @return [String] a header value
def self.header(type, token)
case token
when String, Symbol
"#{type} #{token}"
when Hash
build_hash(type.to_s, token)
else
raise ArgumentError,
"Can't build an Authorization #{type}" \
"header from #{token.inspect}"
end
end

# @param type [String]
# @param hash [Hash]
# @return [String] type followed by comma-separated key=value pairs
# @api private
def self.build_hash(type, hash)
comma = ', '
values = []
hash.each do |key, value|
values << "#{key}=#{value.to_s.inspect}"
end
"#{type} #{values * comma}"
end

# @param app [#call]
# @param type [String, Symbol] Type of Authorization
# @param token [String, Symbol, Hash] Token value for the Authorization
def initialize(app, type, token)
@header_value = self.class.header(type, token)
# @param params [Array<String, Proc>] parameters to build the Authorization header.
# If the type is `:basic`, then these can be a login and password pair.
# Otherwise, a single value is expected that will be appended after the type.
# This value can be a proc, in which case it will be invoked on each request.
def initialize(app, type, *params)
@type = type
@params = params
super(app)
end

# @param env [Faraday::Env]
def call(env)
env.request_headers[KEY] = @header_value unless env.request_headers[KEY]
@app.call(env)
def on_request(env)
return if env.request_headers[KEY]

env.request_headers[KEY] = header_from(@type, *@params)
end

private

# @param type [String, Symbol]
# @param params [Array]
# @return [String] a header value
def header_from(type, *params)
if type.to_s.casecmp('basic').zero? && params.size == 2
basic_header_from(*params)
elsif params.size != 1
raise ArgumentError, "Unexpected params received (got #{params.size} instead of 1)"
else
value = params.first
value = value.call if value.is_a?(Proc)
"#{type} #{value}"
end
end

def basic_header_from(login, pass)
value = Base64.encode64("#{login}:#{pass}")
value.delete!("\n")
"Basic #{value}"
end
end
end
Expand Down
20 changes: 0 additions & 20 deletions lib/faraday/request/basic_authentication.rb

This file was deleted.

20 changes: 0 additions & 20 deletions lib/faraday/request/token_authentication.rb

This file was deleted.

23 changes: 0 additions & 23 deletions spec/faraday/connection_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -141,28 +141,6 @@
end
end

describe 'basic_auth' do
subject { conn }

context 'calling the #basic_auth method' do
before { subject.basic_auth 'Aladdin', 'open sesame' }

it { expect(subject.headers['Authorization']).to eq('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==') }
end

context 'adding basic auth info to url' do
let(:url) { 'http://Aladdin:open%20sesame@sushi.com/fish' }

it { expect(subject.headers['Authorization']).to eq('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==') }
end
end

describe '#token_auth' do
before { subject.token_auth('abcdef', nonce: 'abc') }

it { expect(subject.headers['Authorization']).to eq('Token nonce="abc", token="abcdef"') }
end

describe '#build_exclusive_url' do
context 'with relative path' do
subject { conn.build_exclusive_url('sake.html') }
Expand Down Expand Up @@ -605,7 +583,6 @@

context 'after manual changes' do
before do
subject.basic_auth('', '')
subject.headers['content-length'] = 12
subject.params['b'] = '2'
subject.options[:open_timeout] = 10
Expand Down
44 changes: 15 additions & 29 deletions spec/faraday/request/authorization_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
RSpec.describe Faraday::Request::Authorization do
let(:conn) do
Faraday.new do |b|
b.request auth_type, *auth_config
b.request :authorization, auth_type, *auth_config
b.adapter :test do |stub|
stub.get('/auth-echo') do |env|
[200, {}, env[:request_headers]['Authorization']]
Expand All @@ -14,18 +14,18 @@

shared_examples 'does not interfere with existing authentication' do
context 'and request already has an authentication header' do
let(:response) { conn.get('/auth-echo', nil, authorization: 'Token token="bar"') }
let(:response) { conn.get('/auth-echo', nil, authorization: 'OAuth oauth_token') }

it 'does not interfere with existing authorization' do
expect(response.body).to eq('Token token="bar"')
expect(response.body).to eq('OAuth oauth_token')
end
end
end

let(:response) { conn.get('/auth-echo') }

describe 'basic_auth' do
let(:auth_type) { :basic_auth }
let(:auth_type) { :basic }

context 'when passed correct params' do
let(:auth_config) { %w[aladdin opensesame] }
Expand All @@ -44,43 +44,29 @@
end
end

describe 'token_auth' do
let(:auth_type) { :token_auth }

context 'when passed correct params' do
let(:auth_config) { 'quux' }

it { expect(response.body).to eq('Token token="quux"') }

include_examples 'does not interfere with existing authentication'
end
describe 'authorization' do
let(:auth_type) { :Bearer }

context 'when other values are provided' do
let(:auth_config) { ['baz', { foo: 42 }] }
context 'when passed a string' do
let(:auth_config) { ['custom'] }

it { expect(response.body).to match(/^Token /) }
it { expect(response.body).to match(/token="baz"/) }
it { expect(response.body).to match(/foo="42"/) }
it { expect(response.body).to eq('Bearer custom') }

include_examples 'does not interfere with existing authentication'
end
end

describe 'authorization' do
let(:auth_type) { :authorization }

context 'when passed two strings' do
let(:auth_config) { ['custom', 'abc def'] }
context 'when passed a proc' do
let(:auth_config) { [-> { 'custom_from_proc' }] }

it { expect(response.body).to eq('custom abc def') }
it { expect(response.body).to eq('Bearer custom_from_proc') }

include_examples 'does not interfere with existing authentication'
end

context 'when passed a string and a hash' do
let(:auth_config) { ['baz', { foo: 42 }] }
context 'when passed too many arguments' do
let(:auth_config) { %w[baz foo] }

it { expect(response.body).to eq('baz foo="42"') }
it { expect { response }.to raise_error(ArgumentError) }

include_examples 'does not interfere with existing authentication'
end
Expand Down

0 comments on commit 3fee015

Please sign in to comment.