Skip to content

Instantly share code, notes, and snippets.

@kapkaev
Created March 5, 2013 08:13
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kapkaev/5088751 to your computer and use it in GitHub Desktop.
Save kapkaev/5088751 to your computer and use it in GitHub Desktop.
Faraday::Request::DigestAuth
require 'faraday'
require 'net/http/digest_auth'
module Faraday
# Public: A Faraday middleware to use digest authentication. Since order of
# middlewares do care, it should be the first one of the Request middlewares
# in order to work properly (due to how digest authentication works).
#
# If some requests using the connection don't need to use digest auth you
# don't have to worry, the middleware will do nothing.
#
# It uses Net::HTTP::DigestAuth to generate the authorization header but it
# should work with any adapter.
#
# Examples:
#
# connection = Faraday.new(...) do |connection|
# connection.request :digest, USER, PASSWORD
# end
#
# # You can also use it later with a connection:
# connection.digest_auth('USER', 'PASSWORD')
#
class Request::DigestAuth < Faraday::Middleware
# Public: Initializes a DigestAuth.
#
# app - The Faraday app.
# user - A String with the user to authentication the connection.
# password - A String with the password to authentication the connection.
def initialize(app, user, password)
super(app)
@user, @password = user, password
end
# Public: Sends a first request with an empty body to get the
# authentication headers and then send the same request with the body and
# authorization header.
#
# env - A Hash with the request environment.
#
# Returns a Faraday::Response.
def call(env)
response = handshake(env)
return response unless response.status == 401
env[:request_headers]['Authorization'] = header(response)
@app.call(env)
end
private
# Internal: Sends the the request with an empry body.
#
# env - A Hash with the request environment.
#
# Returns a Faraday::Response.
def handshake(env)
env_without_body = env.dup
env_without_body.delete(:body)
@app.call(env_without_body)
end
# Internal: Builds the authorization header with the authentication data.
#
# response - A Faraday::Response with the authenticate headers.
#
# Returns a String with the DigestAuth header.
def header(response)
uri = response.env[:url]
uri.user = @user
uri.password = @password
realm = response.headers['www-authenticate']
method = response.env[:method].to_s.upcase
Net::HTTP::DigestAuth.new.auth_header(uri, realm, method)
end
end
class Connection
# Public: Adds the digest auth middleware at the top and sets the user and
# password.
#
# user - A String with the user.
# password - A String with the password.
#
def digest_auth(user, password)
self.builder.insert(0, Faraday::Request::DigestAuth, user, password)
end
end
end
# Register the middleware as a Request middleware with the name :digest
Faraday.register_middleware :request, digest: Faraday::Request::DigestAuth
@bhaberer
Copy link

bhaberer commented Nov 6, 2013

Thanks for writing this, it was super handy.

I went ahead and made this into a gem and upped it to Rubygems.

I added you as an author in the gemspec, but let me know if you want added to the repo or made a gem owner.

@noam87
Copy link

noam87 commented Jul 16, 2014

Thanks!

@oriolgual
Copy link

Actually, this seems a copy of hyperclient's implementation (but without the tests): See codegram/hyperclient@a47d7db and codegram/hyperclient@e443018#diff-05401ebefab536882f566b668412c798L2

@dblock
Copy link

dblock commented Sep 26, 2014

I made a PR bhaberer/faraday-digestauth#6 acknowledging the origins of the gem and porting tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment