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:
@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.
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