Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
335 lines (241 sloc) 9.73 KB

Authentication with OmniAuth

Basic Demo

Demo Goals

Auth Options

Local Faves


"OmniAuth is a Ruby authentication framework aimed to abstract away the difficulties of working with various types of authentication providers. It is meant to be hooked up to just about any system, from social networks to enterprise systems to simple username and password authentication."

  • Implemented using a base gem, and pluggable strategies.
  • Each strategy is a different gem.
  • Does not provide user models, or other convenience methods (e.g. logged_in?, current_user)
  • Works with Devise, CanCan, etc.

Create Project, Install OmniAuth & Identity

rails new Project

Add to Gemfile and bundle:

gem 'bcrypt-ruby', '~> 3.0.0'

gem 'omniauth'

gem 'omniauth-identity'

Now add the rack middleware and options in an initializer. You can specify what fields to care about, and these show up in the default form.


Rails.application.config.middleware.use OmniAuth::Builder do
    provider :identity, :fields => [:name, :email]

Identity Model

OmniAuth::Identity is implemented like an external provider, but it is resident in your app. Stores email, hashed password, and potentially other info in a model (many persistence options).

Make an Identity model:

rails g model identity email:string password_digest:string

Update Identity model to inherit from:


Add attribute access (this is wrong on the identity gem docs):

attr_accessible :email, :password, :password_confirmation

OmniAuth uses the /auth/:provider path as the link to follow to start the authentication process for a provider.

Can test by going to http://localhost:3000/auth/identity/register


Need a sessions controller and actions for new, create, destroy, failure:

rails g controller Sessions new create destroy failure

Session new page that links to: /auth/identity/register

After you register, tries to take you to a strategy specific callback: /auth/identity/callback


match '/auth/:provider/callback', :to => 'sessions#create'
match '/auth/failure', :to => 'sessions#failure'
match '/logout', :to => 'sessions#destroy', :as => 'logout'

root :to => 'sessions#new'

This is a bit sneaky: sessions#create method for all providers, but could have done one per provider, if that would have helped.

Also - when there is an error - a rack var has the login info:

def new
    @identity = env['omniauth.identity']

Users and Authentications

It's a good practice to have a many-to-one relationship between User and Auhentications.

Devise works this way with OmniAuth, and it makes sense with the goal of one Users with many external identities.

An authentication has the relationship to the user, the name/identifier of the provider, and the UID for the user from thaty provider. This works for the identity strategy, and external strategies (e.g. {user_id:[], provider:'identity', oid:[]}):

rails g model authentication user_id:integer provider:string uid:string

Users should be about the person, and have info about them as domain objects with biz rules, not about how they login to some site:

rails g model user name:string

Now we need to implement creating the authentication, user, and logging in.

We will implement it to work for identity, but should be very similar to how it works for other providers. The major difference should be in what data comes back from the provider, and how that is used to fill out the models.

Some login basics (if you aren't using Devise, for example):


class ApplicationController < ActionController::Base
    helper_method :current_user, :logged_in?

    def current_user
        @current_user ||= User.find(session[:user_id]) if session[:user_id]

    def current_user=(user)
        session[:user_id] =
        @current_user = user

    def logged_in?

Show when we are logged in


<% if logged_in? %>

    <b><%= %></b> is logged in.

<% else %>
<% end %>

Finding or Creating Users/Authentications


def create
    auth_hash = request.env['omniauth.auth']
    authentication = Authentication.find_or_create_from_auth_hash(auth_hash)
    self.current_user = authentication.user
    redirect_to root_url, :notice => "Logged in successfully."

The 'omniauth.auth' hash, set by omniauth rack middleware, has the info returned from a succssful authentication.

  • auth_hash['provider'] is set to the string name of the provider

  • auth_hash['uid'] is set to some unique id for the user scoped by the provider

Other hashes are usually included, but vary by provider and strategy implementation.

  • auth_hash['info'] typically has basic user info (login, email)

  • auth_hash['extra'] can include this as well, or used for extended profile info

  • auth_hash['credentials'] typically has an access token and expiry for OAuth2 providers

Use these in the models to find/create the Authentication & User.


def self.find_or_create_from_auth_hash(auth_hash)
    auth = find_by_provider_and_uid(auth_hash['provider'], auth_hash['uid'].to_s)
    unless auth
        user = User.find_or_create_from_auth_hash(auth_hash)

        auth_create_attributes = {
            :provider   => auth_hash['provider'],
            :uid        => auth_hash['uid']

        auth = user.authentications.create!(auth_create_attributes)


def self.find_or_create_from_auth_hash(auth_hash)
    info = auth_hash['info']
    name = info['name'] || info['email']

Adding another provider: omniauth-github

Here is the gem:

For oauth, nice to be deployed to register an app, so I have the demo app on heroku:

Add the strategy gem and bundle:

gem 'omniauth-github'

Add it to the config/init

provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], :scope => 'user,repo,gist'

Now go create an app on github, and get the key and secret:

Set these in your local env, and on heroku:

.rvmrc (or equivalent)

export GITHUB_KEY=abcdefghijklmnop
export GITHUB_SECRET=abcdefghijklmnopabcdefghijklmnopabcdefghijklmnop

heroku cmd line:

heroku config:add GITHUB_KEY=abcdefghijklmnop
heroku config:add GITHUB_SECRET=abcdefghijklmnopabcdefghijklmnopabcdefghijklmnop

Add a link to github from the sessions#new page:

<h3>External Providers</h3>
<p><%= link_to "login", "/auth/github" %> with github.</p>

That ought to do it - pretty much everything else we wrote carries over!

FYI - I temporarily logged the auth_hash, to give a sense of what is in there:

    --- !map:OmniAuth::AuthHash 
    provider: github
    uid: "46439"
    info: !map:OmniAuth::AuthHash::InfoHash 
        nickname: kookster
        email: andrew_AT_beginsinwonder_DOT_com
        name: Andrew Kuklewicz
        urls: !map:Hashie::Mash 
    credentials: !map:Hashie::Mash 
        token: abcdefghiklmnopqrstuvwxyz
        expires: false
    extra: !map:Hashie::Mash 
        raw_info: !map:Hashie::Mash 
            type: User
            login: kookster
            owned_private_repos: 0
            followers: 11
            created_at: "2009-01-14T06:39:27Z"
            email: andrew_AT_beginsinwonder_DOT_com
            disk_usage: 59516
            plan: !map:Hashie::Mash 
                private_repos: 0
                space: 307200
                name: free
                collaborators: 0
            public_gists: 1
            hireable: false
            private_gists: 0
            following: 8
            name: Andrew Kuklewicz
            bio: Tech Dir @
            collaborators: 0
            public_repos: 10
            id: 46439
            total_private_repos: 0
            location: Boston, MA
            gravatar_id: abcdefghiklmnopqrstuvwxyz


Jump to Line
Something went wrong with that request. Please try again.