HTTP Basic Authentication with Devise
by ewout
Devise is becoming a popular gem for adding modular authentication to a rails application. It builds on top of warden, which provides a pluggable architecture for multiple authentication strategies at the rack level.
Since the cool kids are using it, we did not want to be left out and ported our main application from restful_authentication. Aside from being cool, the switch will make porting to rails 3 easier, the latest devise is compatible.
The application uses basic http authentication for private RSS feeds and ical subscriptions. This is pretty common at the service level of an application, machines do not like login forms. Devise works with basic authentication out of the box, but it will only work when the authentication headers are already present in the request. When they are not, devise will return a 302 redirect to the login form and the RSS reader gives up.
The solution is to create a new devise strategy in config/initializers/devise.rb
class HttpAuthenticatableNonHtml < Devise::Strategies::HttpAuthenticatable def valid? not request_format.html? or super end def http_authentication super or '' end end Warden::Strategies.add(:http_auth_non_html, HttpAuthenticatableNonHtml)
Warden needs to be instructed to use the strategy, inside the Devise.config block.
config.warden do |manager| manager.default_strategies.unshift :http_auth_non_html end
The strategy will return a 401 with authentication realm when accessing a protected resource that is not html.
How warden and devise work in rails
Figuring out this solution required diving into the warden an devise code, which is quite intimidating at first. I created a diagram that hopefully makes it easier to understand the basic working of the authentication stack.
- The HTTP request enters the rack stack.
- Warden gets the request and forwards it in the rack stack, adding an environment variable “warden” that points to an authentication proxy.
- The request gets dispatched to the rails controller, which may call authenticate_user! from a filter. This is an alias for request.env['warden'].authenticate!(:scope => :user).
- The warden proxy picks an authentication strategy. Any strategy for which valid? returns true is tried.
- When authentication succeeds, a user object is returned to the controller. When it fails, the symbol :warden is thrown down the stack, and caught by the warden rack application. The latter will return a response, which is a redirect to the login page by default. This can be overridden by calling warden.custom_response!.
Comments
Thanks for publishing this.
Thank you! Very helpful and informative.
Brilliant and simple tip. We’re not using it for RSS but for an API which needs authentication – same principle.
I especially like how easy it was to make the HTTP Basic Auth only apply to non HTML requests – I don’t think I’d even have expected that if you hadn’t included it already! Thanks
[...] Fakeweb and get a list of responses from the external API an add different test cases. References http://ewout.name/2010/04/http-basic-authentication-with-devise/ https://github.com/plataformatec/devise Share this:TwitterFacebookLike this:LikeBe the first to [...]