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:

gem 'omniauth' 1 gem & #39;omniauth'

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 1 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 config/routes.rb , add:

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 1 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 1 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 ApplicationController :

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 1 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 1 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 1 module Admin class UsersController < ApplicationController before_filter : admin_required def index render : text = > 'Hello from the admin panel!' end end end