January 14, 2008 by Adam Wiggins
Backstory: A Fiery Debate
Writing a user model and the standard login authentication code seems like busywork to a lot of coders. In fact, many people expected a next-generation app framework such as Rails to handle this for you. After all, Django does. Initially the login engine for Rails seemed to fill this slot, but following a fair amount of controversy over best practices, the login engine was killed by its creator.
With our BDfL having forever cursed prebuilt login systems, the Rails community mostly stopped trying to make them. Yet, this puts us back at square one: developers are annoyed at the amount of boilerplate busywork that is necessary for almost every web app they write.
acts_as_authencated is the halfway solution that is now popular: it’s a generator, not a drop-in component, so it spits out the boilerplate for you, and then you can modify it. And then of course there’s the idea that logins shouldn’t be maintained by individual sites at all, but stored someplace in the ownership of users. OpenID is the great hope here, but while we wait for this technology to mature (and gain acceptance with less technical audiences), maintaining user logins will continue to be a part of building web apps.
The debate over how to create login authentication will continue to smoulder for some time yet. But in the meantime, Heroku now offers a user login solution that will be handy for apps shared with a small number of people, and requires almost no code.
Apps created on Heroku are already shared with some number of users, specified by their email addresses (this works the same as other types of collaborative editing apps, such as Google Docs). Since these users are already logging in to access the app, wouldn’t it be handy if you could find out from the Heroku backend who was logged into your app?
We thought so too. Which why we’ve created the
heroku_user helper object. It’s a small feature, but a surprisingly convenient one. I’ve already found it quite useful in some of my own personal apps. Our company wiki, for example, uses this method. So how does it work?
Within any controller, helper, or view, you can access the
heroku_user object, which has the methods
heroku_user info of logged in heroku users who visit the site anonymously.)
logged_in? checks if
can_edit? returns true if the user is a collaborator on the app, or false if they only have view access. Take note: this refers to the capability to edit the source code. They can still write to your app through its regular web interface. If you are using a standard scaffold, for example, users will be able to add and remove data like always.
can_edit? == true means that they can flip to the Heroku code editor and start tinkering under the hood.
Let’s say you’re writing the quintessential Rails app, a blog. Rather than having an
author_id on your posts, you’d instead just have a string field
author which is filled in with the email address from
heroku_user. Creating a post might be:
def create @post = Post.new(params[:post]) @post.author = heroku_user.email if @post.save ...
Validating that a user can only delete their own posts would be:
def create @post = Post.find_by_id_and_email(params[:id], heroku_user.email) if @post @post.destroy ...
If you want to show the current user in your layout somewhere, that’s a cinch:
<% if heroku_user.logged_in? %> Welcome, <%= heroku_user.email %> <% end %>
Storing Your Own Data
What about storing data for the user, such as app-specific preferences? Using
heroku_user, you have no user model. One approach that I’ve used in my own apps is to create a user model on the fly, based on the
heroku_user value. That is, something like:
before_filter :create_user_mirror def create_user_mirror @user = User.find_by_email(heroku_user.email) || User.create!(:email => heroku_user.email) end
But wait, wasn’t the point to avoid creating our own user model? Well, sure – but if you have some extra data you need to attach, this is only a few lines in your application controller and a model with just one field. You don’t have to deal with user management, password reminders, or password salting. Avoiding responsibility for user passwords is nice for a smallish app that you’re throwing together quickly, and may only be shared with a limited number of people anyway.
It’s Not For Everyone
This is probably not a general-purpose solution to the login problem. It is, however, a solution that may prove convenient in many situations. If you’re making a public app which is expecting a large user base, you’ll want complete control over the login process. In that case, you might use the Heroku user info as temporary scaffolding to get you off the ground, so that you can focus on more interesting parts of the app initially. Later on you can come back and write the boring old login code, same as always.
But if you’re writing a prototype, or an app for internal use at your company, or even just to share with a few friends or family members, then heroku_user can save you time and let you get straight to coding the business logic of your app.