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

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

Closed
mscoutermarsh opened this issue Aug 31, 2021 · 16 comments · Fixed by #1320
Closed

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

mscoutermarsh opened this issue Aug 31, 2021 · 16 comments · Fixed by #1320

Comments

@mscoutermarsh
Copy link

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'
@peterberkenbosch
Copy link

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

@olleolleolle
Copy link
Member

olleolleolle commented Sep 1, 2021

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

@iMacTia
Copy link
Member

iMacTia commented Sep 1, 2021

@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

@mscoutermarsh
Copy link
Author

Thank you! All good now. ❤️

@xiaoxipang
Copy link

xiaoxipang commented Sep 2, 2021

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?

@iMacTia
Copy link
Member

iMacTia commented Sep 2, 2021

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 😄

@xiaoxipang
Copy link

xiaoxipang commented Sep 2, 2021

@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!

@paukul
Copy link

paukul commented Sep 9, 2021

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.

@mscoutermarsh
Copy link
Author

mscoutermarsh commented Sep 9, 2021

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

@iMacTia
Copy link
Member

iMacTia commented Sep 13, 2021

@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

@paukul
Copy link

paukul commented Sep 13, 2021

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

@iMacTia
Copy link
Member

iMacTia commented Sep 13, 2021

No worries at all! Glad it worked and thanks for coming back and confirming 🙏

@rgaufman
Copy link

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?

@iMacTia
Copy link
Member

iMacTia commented Dec 21, 2021

@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?

@jjb
Copy link
Contributor

jjb commented Mar 18, 2024

@iMacTia
Copy link
Member

iMacTia commented Mar 18, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants