Under the hood: Not-so-basic authentication
Recently I worked on a project that required a single login to access administration options. There was no need for a full-blown RESTful authentication solution – I was advised to “Just use basic auth!”
Rails makes it easy. You probably know the standard example. You put a before_filter :authenticate in the controllers that require it, and set it up in the Application Controller.
def authenticate
authenticate_or_request_with_http_basic do |user, password|
user == 'admin' && password == 'pass'
end
end
It’s all well and good … until you want to add a log out button. The browser stores the successful login credentials in a sort of cookie, and applies them to every page which requests basic authentication. Once you’ve logged in, it’s actually quite hard to make the browser forget you until you quit and restart the browser. It’s hard, but not impossible. If you can force basic authentication to fail, the browser will throw away the credentials.
So the solution is to add a session variable that says “NO SRSLY, LOG ME OUT PLS!” This is the logout action (a destroy method in a Sessions Controller)
def destroy
session[:logout_requested] = true
flash[:notice] = "You have logged out successfully"
redirect_to(root_path)
end
Now for the tricky bit. The way this works is subtle and takes a moment to figure out each time I think about it. We change the authenticate method in the Application Controller so that as well as checking the username and password, it also ensures that this flag has not been set. Meaning we can cause basic authentication to fail when we want it to.
def authenticate
authenticate_or_request_with_http_basic do |user, password|
user == 'admin' && password == 'pass' && session[:logout_requested] != true
end
session[:logout_requested] = nil
end
Next time our user goes to a page which requires authentication, the browser still provides the correct username and password, but the flag causes the basic authentication to fail. Obviously we then have to clear the flag straight away, otherwise the user would not be able to get back in again even with the correct credentials. The user must type in the correct login name and password again to be able to get back in.
Perhaps we want to know whether the user is logged in or not, so that we know whether to display an edit button. We can set another session variable. Conveniently, authenticate_or_request_with_http_basic returns a boolean value.
def authenticate
session[:logged_in] = authenticate_or_request_with_http_basic do |user, password|
user == 'admin' && password == 'pass' && session[:logout_requested] != true
end
session[:logout_requested] = nil
end
Remember to set the flag to false when you log out. Also remember that this flag could be true, false or nil so a check in the Application Controller looks like this:
def logged_in?
session[:logged_in] == true
end
Finally it’s worth noting that the username and password do not have to be hard-coded like this. It’s simple for an example, but don’t think that’s all there is to basic authentication. There’s nothing to stop you comparing against values in a settings table or even doing a user lookup à la RESTful authentication.
def authenticate
session[:logged_in] = authenticate_or_request_with_http_basic do |email, password|
user = User.authenticate(email, password)
if user && session[:logout_requested] != true
self.current_user = user
true
else
self.current_user = nil
false
end
end
session[:logout_requested] = nil
end
Thanks to Richard and Tris for their help in figuring out the not-so-basic aspects of basic authentication! :)

Fantastic! Solved an immediate problem for me.
I wouldn't mind if the example was more pedantic, and more generic. I may post that on my blog.
I'm glad it helped you out. Hopefully it shows that there is much more that can be done with basic authentication than people sometimes realise.
Let me know if you post a follow-on. I'll be interested to read it.
There must be some strange behaviour going on with Safari, since I can get Rails to logout but revisiting the protect page and it let's me right in.
Still – the admin links are hidden, and the auth is destroyed when the browser quits, so it's not all bad.
Of course, nothing beats proper authentication rather than digest.
As a newbie to this designing and coding world, it's an awesome find to mingle with great minds here. May I take this opportunity to ask, is there a way to make things as simple as ABC especially if we are dealing with an error logging in and instead of going back from scratch, there should be a message of where the error lies so users will not go all over again: typing from the very beginning?
I think that the solution works good (so long as you close your eyes when looking at the passwords stored in the PL/SQL). The need for an ORACLE wallet is a bit smelly but that seems to be just how it works. One thing I did miss about that is the wallet needs to be stored in a location the server
It’s simple for an example, but don’t think that’s all there is to basic authentication. There’s nothing to stop you comparing against values in a settings table or even doing a user lookup à la RESTful authentication.
Thanks and Regards