In my Rails 2.3 app we have an admin section that is protected by basic HTTP authentication. This has worked on our production and staging environments for years. Recently I setup a new environment to demo a long-running feature branch, and to differentiate between that and the other staging server I set it to run using a custom "redesign" environment. So now my Admin::BaseController
looks like this:
class Admin::BaseController < ApplicationController
before_filter :verify_access
def verify_access
logger.info "\n\n-- running verify_access filter on #{RAILS_ENV}\n"
if %w(production staging redesign).include?(RAILS_ENV)
logger.info "\n-- should send authentication\n"
authenticate_or_request_with_http_basic("Restricted Access") do |username, password|
logger.info "\nauthentication received: {username}::#{password}\n"
username == 'myusername'
password == 'mypassword'
end
end
end
end
For some reason, the new server ALWAYS responds Unauthorized and doesn't offer any login dialog, not even if I quit and reopen my browser, not even if I use a private browser session, not even if I specify a username in the URL (i.e. http://username@www.mysite.com/admin
). It just automatically redirects me to the root and adds ?unauthorized=true
to the query string, though no where in my application code is there anything that would do that.
I know it's hitting the verify_access filter, because I can see it in the log file:
Processing Admin::OrdersController#index (for xx.xx.xx.xx at 2014-05-09 05:56:59) [GET]
Parameters: {"action"=>"index", "controller"=>"admin/orders"}
-- running verify_access filter on redesign
-- should send authentication
Filter chain halted as [:verify_access] rendered_or_redirected.
Completed in 25ms (View: 21, DB: 1) | 401 Unauthorized [http://www.mydomain.com/admin/orders]
Processing IndexController#show (for xx.xx.xx.xx at 2014-05-09 05:56:59) [GET]
Parameters: {"action"=>"show", "controller"=>"index", "unauthenticated"=>"true"}
Rendering index/show
Completed in 30ms (View: 27, DB: 3) | 200 OK [http://www.mydomain.com/?unauthenticated=true]
In the networking panel in Chrome, the request for the admin page actually shows that it's receiving a 302 Found response from the server, even though Rails is sending a 401 Unauthorized.
GET /admin/orders HTTP/1.1
Host: www.mydomain.com
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: _myapp_session=xxx
HTTP/1.1 302 Found
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: keep-alive
Status: 302
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 2.2.10
Location: /?unauthenticated=true
Server: nginx/0.7.65 + Phusion Passenger 2.2.10 (mod_rails/mod_rack)
The new server is a clone of the original staging server, so the architecture is exactly the same, and all the same chef recipes were used. Any ideas on what's going on here?
Turns out HTTP Basic Auth was tripping over Devise, one of the new features in this branch. Updating my verify_access
method to the following solved the problem:
def verify_access
if %w(production staging redesign).include?(RAILS_ENV)
authenticate_or_request_with_http_basic("Restricted Access") do |username, password|
username == 'username' && password == 'password'
end
end
warden.custom_failure! if performed?
end