lostisland/faraday

WARNING: `Faraday::Connection#basic_auth` is deprecated

Closed this issue ยท 16 comments

Basic Info

  • Faraday Version: 1.7.1
  • Ruby Version: 3.0.2

Issue description

Saw the deprecation warning, updated code to match the documented basic auth instructions. The documented code throws a number of arguments error.

Not sure if the docs are wrong or it's an issue with the gem.

Steps to reproduce

http = Faraday.new do |conn|
  conn.request(:authorization, :basic, 'username', 'password')
end

http.post("https://example.com")
ArgumentError: wrong number of arguments (given 4, expected 3)
from /Users/mscoutermarsh/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/faraday-1.7.1/lib/faraday/request/authorization.rb:43:in `initialize'

The docs are wrong :) I just opened a PR to fix that.

In the meantime you need to use it like this:

http = Faraday.new do |conn|
  conn.request(:basic_auth, 'username', 'password')
end

Hello! The context for the documentation website not rhyming with the 1.x deprecation message is: "all the website documents is the upcoming 2.0". That version where the old way is removed.

The context is only ever mentioned only in the README - this is a weakness, which we'd love to get concrete help with on the website front.

Link to that part:

https://github.com/lostisland/faraday#attention

@peterberkenbosch is correct, that's the right way to use the middleware in Faraday 1.x, while the documentation is showing the correct usage for the upcoming v2.0.

Sorry about the confusion, this is not the first time an issue like this has been raised. I'll be looking at a quick solution to make things clearer

Thank you! All good now. โค๏ธ

Hello @iMacTia,

I still have problem on this issue.
I'm using Faraday 1.7.1 and Ruby 2.7.2.

I originally created a connection like this

client = Faraday.new(url: url) do |faraday|
    faraday.headers["Accept"] = "application/json"
    faraday.headers["X-Version"] = "2"
    faraday.headers["Authorization"] = "token #{token}"
    faraday.request(:json)
    faraday.response(:json, content_type: /\bjson$/)
    faraday.adapter(Faraday.default_adapter)
  end

Based on the above talk, I guess if I switch to use faraday.request(:token_auth, token), it should help add header in the request, so I switched to

client = Faraday.new(url: url) do |faraday|
    faraday.headers["Accept"] = "application/json"
    faraday.headers["X-Version"] = "2"
    faraday.request(:token_auth, token)
    faraday.request(:json)
    faraday.response(:json, content_type: /\bjson$/)
    faraday.adapter(Faraday.default_adapter)
end

The above client is used in this way and I got error "errors"=>["Authentication Required"]

client.get(path, {"sort"=>"created_at", "direction"=>"desc", "per_page"=>100})

I also debugged the code and somehow, middleware didn't take effect in the request and no Authorization in the header, here is one request object dump

#<struct Faraday::Request http_method=:get, path="hide/the/path", params={"sort"=>"created_at", "direction"=>"desc", "per_page"=>100}, headers={"Accept"=>"application/json;", "X-Version"=>"2", "User-Agent"=>"Faraday v1.7.1"}, body=nil, options=#<Faraday::RequestOptions (empty)>>

Did I use it in a wrong way or should I create a new bug?

BTW, what is the difference between faraday.headers["Authorization"] = "token #{token}" and faraday.request(:token_auth, token). Based on my newbie ruby knowledge and reading the source code, token_auth is trying to set the header, which is equivalent to faraday.headers[]=. Then in the doc, why do we recommend to use middleware instead of directly set the header?

Hi @xiaoxipang, lot's of good questions in there, let me see if I can answer them all.

Did I use it in a wrong way or should I create a new bug?

You used it correctly, but I suspect the output of the middleware is not exactly what the server is expecting.
This "token auth" looks like a pretty old standard that is not being used anymore and has been replaced by Bearer token and other mechanisms. The header output in your example would be Authorization: Token token=token, note the token= prefix, which I suspect your sever is not expecting. To fix that, you can instead do the following:

client = Faraday.new(url: url) do |faraday|
    # This header is not necessary, it will be set by the json middleware
    # faraday.headers["Accept"] = "application/json"
    faraday.headers["X-Version"] = "2"
    faraday.request(:authorization, 'Token', token)
    faraday.request(:json)
    faraday.response(:json, content_type: /\bjson$/)
    faraday.adapter(Faraday.default_adapter)
end

This will set the header correctly to Authorization: Token token (no prefix).
Please give it a try and let me know if it works!

I also debugged the code and somehow, middleware didn't take effect in the request and no Authorization in the header, here is one request object dump

That is strange, I just tested the code above and it works as expected for me. Seeing the struct Faraday::Request in the debugging I suspect you might be looking at the object too early, before the header is set by the middleware.
If you run a request and check the response, you should definitely see it there:

client.get('https://google.co.uk')
 => #<Faraday::Response:0x00007fdd01ad1340
  @on_complete_callbacks=[],
  @env=#<Faraday::Env
    @method=:get
    @url=#<URI::HTTPS https://google.co.uk/>
    @request=#<Faraday::RequestOptions (empty)>
    @request_headers={"X-Version"=>"2", "User-Agent"=>"Faraday v1.7.1", "Authorization"=>"Token token"}
    ...

Or another way is to add the faraday.response :logger middleware JUST BEFORE your adapter, this way it will log the request after all the middleware have taken effect and you should definitely see it.

BTW, what is the difference between faraday.headers["Authorization"] = "token #{token}" and faraday.request(:token_auth, token). Based on my newbie ruby knowledge and reading the source code, token_auth is trying to set the header, which is equivalent to faraday.headers[]=. Then in the doc, why do we recommend to use middleware instead of directly set the header?

The main reason for that suggestion is that middleware usually provides greater functionality.
For example, the token_auth middleware allows you to set token properties as well and format them automatically:

faraday.request(:token_auth, 'token', {prop1: value1, prop2: value2})
# This produces `Authorization: Token token=token prop1=value1 prop2=value2

In a similar way, the basic_auth middleware automatically does a Base64 encoding of your user/pass pair.

I agree the faraday.request(:authorization, 'Token', token) middleware above is not doing much more than manually setting the header, but for example in Faraday v2 that middleware accepts a lambda/proc for the token argument and automatically resolves that on each request. That allows you to have a TokenStorage class and dynamically change the token between different requests. With the other approach, you'd need to manually inject the header on each request or recreate the connection every time.

I hope this answers your question, but in short you're good to use both methods, so use the one you like most ๐Ÿ˜„

@iMacTia Thank you so much for the help and detailed explanation!

Please give it a try and let me know if it works!

faraday.request(:authorization, "token", token) works for me.

Or another way is to add the faraday.response :logger middleware JUST BEFORE your adapter, this way it will log the
request after all the middleware have taken effect and you should definitely see it.

It is super nice to know how to debug from now on. And by using that, you are fully correct:
When use faraday.request(:authorization, "token", token), the header is Authorization: "token token_value"
When use faraday.request(:token_auth, token), the header is Authorization: "Token token=\"token_value\""

That is strange, I just tested the code above and it works as expected for me. Seeing the struct Faraday::Request in the debugging I suspect you might be looking at the object too early, before the header is set by the middleware.

Right, I put breakpoint in connection#run_request and at that moment, middleware haven't been called. I add a new break point in Faraday::Request::Authorization and now I can see how it work.

I hope this answers your question, but in short you're good to use both methods, so use the one you like most

Yes and thank you again!

how to get rid of the deprecation warning for now though?

      builder.request :basic_auth, @client_id, @client_secret

still raises the warning for me on 1.7.1 and, as @mscoutermarsh mentioned, the new syntax isn't available yet.

@paukul

http = Faraday.new do |conn|
  conn.request(:basic_auth, 'username', 'password')
end

Fixed it for us. Looks the same as yours, not sure why it's still giving you the warning.

@paukul as @mscoutermarsh mentioned already, that line there is most probably not at fault for the warning you're seeing. The warning is only raised if you call the *_auth methods on the connection object

@mscoutermarsh @iMacTia you're right, we had another sneaky line of code I overlooked. Fixes it indeed. Thank you and sorry for the confusion

No worries at all! Glad it worked and thanks for coming back and confirming ๐Ÿ™

Question, so for basic I need:

http = Faraday.new do |conn|
  conn.request(:basic_auth, user, pass)
end

but for Digest, I need:

http = Faraday.new.digest_auth(user, pass)

Wouldn't it make sense to follow the same conventions for both?

@rgaufman I'm not aware of a digest_auth method to be honest. I couldn't find it in the codebase and when I try your code locally I get a undefined method 'digest_auth' for #<Faraday::Connection:0x000000011d43e5e0>.
Are you sure that's not a custom method you defined in your project, or that another gem added?

Just circling back on this, I believe the digest_auth method is coming from this gem.

However, it has recently been brought to my attention that the gem only supports Faraday 0.x and 1.x.
I've reached out to the gem owners to see if we can help updating the gem to support Faraday 2.x as well