Easy Rails Admin Login with Google Apps and OmniAuth
When you're building a web application, there's always the question of how to handle the site-wide administration. You probably have a small list of people at your company that should be able to access it. If you're like Intridea, you also use Google Apps to handle e-mail etc. Using OmniAuth it is trivially simple to set up a simple "admin login system" for your Rails 3 app. Here's how to do it.
Step One: OmniAuth Up and Running
First we'll need to include OmniAuth in our
Gemfile. Just add it like so:
Next, we should configure OmniAuth for our Google Apps login. Note that you can add this even if you're already using a different Google Apps strategy for OmniAuth. Create the file
config/initializers/omniauth.rb and put this in it:
Rails.application.config.middleware.use OmniAuth::Builder do provider :google_apps, OpenID::Store::Filesystem.new('/tmp'), :domain => 'yourcompany.com', :name => 'admin' end
This makes it so that going to
/auth/admin from your application will have you authenticate through your company's Google Apps account (and ONLY your company's accounts will work).
Step Two: Routing
Next let's set up our routes for the admin panel. In this case I'm supposing that you want to scope everything to an 'admin' subdomain and also that all of your controllers are module scoped to admin (i.e.
Admin::UsersController), but you could easily use
namespace to make it a path prefix instead. In
match '/auth/admin/callback', :to => 'sessions#authenticate_admin' constraints :subdomain => 'admin' do scope :module => 'admin', :as => 'admin' do root :to => 'users#index' resources :users # etc. end end
What this has done is created an OmniAuth callback route and a group of routes that are constrained to the "admin" subdomain and will be prefixed with
admin_ in the named routes. Now that we've got the routes set up, let's actually add the controllers!
Step Three: Controllers
First we'll want to make the callback active. Generate a "sessions" controller if you don't already have one, and add this code to it:
class SessionsController < ApplicationController def authenticate_admin auth_hash = request.env['omniauth.auth'] session[:admin_user] = auth_hash['user_info']['email'] if admin? redirect_to '/' else render :text => '401 Unauthorized', :status => 401 end end end
What we do here is take the e-mail that is provided to us by OmniAuth and store it in the session as the
:admin_user. We then check to see if that e-mail has admin priveleges (using a method we'll write in just a moment) and redirect them to the root of the admin section if so. Next we need to add the
admin? helper to
class ApplicationController def admin? session[:admin_user] && (ENV['ADMINS'] || "").split(',').include?(session[:admin_user]) end helper_method :admin? def admin_required redirect_to '/auth/admin' unless admin? end end
This checks to see if our
:admin_user is in a comma-separated list of approved administrators set at the environment level (a useful strategy for Heroku deployments, but you could do lots of things here such as load a list from YAML etc.). We've also written a method that will be used as a
before_filter to require admin login.
Next up let's actually make our protected resources! If you don't already have it, generate a user admin controller:
rails g controller admin/users
Next up let's just add a simple action to make sure it works:
module Admin class UsersController < ApplicationController before_filter :admin_required def index render :text => 'Hello from the admin panel!' end end end
With that, you're done! When you go to
admin.yourapp.com you should be redirected to log in using Google Apps. Once you've done that, if your name is on the comma-separated list you will be authorized to access the protected controller. This is a simple method that is independent of database storage so can be very useful in just about any application. When OmniAuth makes it so easy, there's no reason not to make your administrative login as smooth and painless as possible!