Third Prestige We make the web work.

Your Website and Email Accounts Should Be Friends, Part II

Nathaniel Jones · February 2, 2013

Great! I want this for my site!

Cool.  Let us walk you through how we implemented this using:

  • Ruby on Rails
  • Heroku, and

  • Devise (a very popular authentication plugin)
We'll also show you a couple of gotchas that you can hopefully avoid.

1. Install the Google Apps Extensions

You'll want to add these 2 gems to your Gemfile:

  gem 'omniauth-google-apps'

  gem 'omniauth'

There are several different Google Apps and OmniAuth libraries out there, but these are the ones that worked for us.

2. Configure OmniAuth

Open 'config/initializers/devise.rb', find "# ==> OmniAuth", and add the following:

  require 'openid/store/filesystem'

  config.omniauth :google_apps, domain: 'example.org',

    store: OpenID::Store::Filesystem.new('./tmp')

You'll want to replace example.org with your company's domain.

Now, your config/routes.rb should look something like this:

    devise_for :users, controllers: {

      omniauth_callbacks: 'users/omniauth_callbacks' }

To redirect to the login page when you visit the Administration page, add this to app/controllers/application_controller.rb:

  rescue_from CanCan::AccessDenied do |exception|

    redirect_to user_omniauth_authorize_url(:google_apps)

  end

And, in app/controllers/users/, add a omniauth_callbacks_controller.rb file:

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

  skip_before_filter :verify_authenticity_token, only: :google_apps

  def google_apps

    @user = User.find_for_googleapps_oauth(request.env["omniauth.auth"], current_user)

    if @user.persisted?

      flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Google"

      cookies[:authenticated] = true # for admin.js

      sign_in_and_redirect @user, :event => :authentication

    else

      session["devise.google_data"] = request.env["omniauth.auth"]

      redirect_to root_url

    end

  end

protected

  def new_session_path *args

    root_path

  end

end

Finally, in app/models/user.rb:

    def self.find_for_googleapps_oauth(access_token, signed_in_resource=nil)

      data = access_token['info']

      self.find_or_create_by_email!(data['email'])

    end

    def self.new_with_session(params, session)

      super.tap do |user|

        if data = session['devise.googleapps_data'] && session['devise.googleapps_data']['user_info']

          user.email = data['email']

        end

      end

    end

Great! That should be enough to get the app up and running! 

SSL-only

You should lock down your admin interface to SSL-only.  If you don't want
to pay for SSL, Heroku provides you with SSL access to every Heroku app,
for free: "<my-app-name>.herokuapp.com".  You can use that URL to
access your CMS.

Work-arounds for the free plan

If you're on Heroku's free, 1-dyno plan, your app will actually timeout and die
when you try to log in.  This is because you make a request to log in,
and your app makes a request to Google, which then makes ANOTHER request
to your website to ensure you are who you say you are.  But your dyno
is tied up waiting to hear back from Google!  So you become locked up in
a stalemate, and the app dies after 30 seconds, no response received.

The easiest way to address this is to use the Unicorn server, which allows you to spool up a few concurrent servers on 1 dyno.  Now,
this may not always be the best idea, depending on how much heavy
lifting your app does.  For a typical website CMS which implements
caching strategies, though, you shouldn't see any problems.

  1. Add 'unicorn' to your Gemfile

  2. Add a Unicorn configuration to config/unicorn.rb:

    worker_processes 4 # number of unicorn workers to spin up

    timeout 30         # restarts workers that timeout

  3. Configure Heroku to use Unicorn by adding this to your Procfile:

    web: bundle exec unicorn -c config/unicorn.rb -E $RACK_ENV -p $PORT config.ru

This will allow your app to respond to Google's request while maintaining a
connection to Google from your original request, so everything can carry
back up the chain, without paying for an additional dyno just so you
can log in once a month.

There you go!

No separate accounts, no passwords, easy banking-level security.  What's not to love.  



Authored_nathaniel-32

Sometimes called a “Unicorn” in the Austin tech community for his broad expertise, Nathaniel is equally at home identifying key business goals, optimizing a website's speed tenfold, or polishing the final UI details that make software shine.

Also he may or may not have flown a 2-seater plane without a pilot's license while down under, so, there's that.