Ruby On Rails Login

Sample Project

Download this sample project configured with your Auth0 API Keys.

System Requirements
  • Ruby 2.3.1
  • Rails 5.0.0
Show requirements

The easiest way to add authentication to your Rails application is to use Auth0's Lock widget and OmniAuth authentication strategy.

Initialize Omniauth Auth0

Create a file named auth0.rb under config/initializers with the following content:

Rails.application.config.middleware.use OmniAuth::Builder do
  provider(
    :auth0,
    'YOUR_CLIENT_ID',
    'YOUR_CLIENT_SECRET',
    'YOUR_AUTH0_DOMAIN',
    callback_path: "/auth/oauth2/callback"
  )
end

NOTE: This tutorial uses omniauth-auth0, a custom OmniAuth strategy.

Add the Auth0 Callback Handler

Use the following command to create the controller that will handle the Auth0 callback:

rails generate controller auth0 callback failure --skip-template-engine --skip-assets

In the newly created controller, add a callback success and failure handler.

class Auth0Controller < ApplicationController
  def callback
    # This stores all the user information that came from Auth0
    # and the IdP
    session[:userinfo] = request.env['omniauth.auth']

    # Redirect to the URL you want after successful auth
    redirect_to '/dashboard'
  end

  def failure
    # show a failure page or redirect to an error page
    @error_msg = request.params['message']
  end
end

Replace the generated routes on routes.rb with the following:

get "/auth/oauth2/callback" => "auth0#callback"
get "/auth/failure" => "auth0#failure"

Trigger Login with Lock

For more information on using Lock see the documentation.

<script src="https://cdn.auth0.com/js/lock/10.12.1/lock.min.js"></script>
<script>
  var lock = new Auth0Lock('YOUR_CLIENT_ID', 'YOUR_AUTH0_DOMAIN', {
    auth: {
      redirectUrl: 'https://YOUR_APP/callback',
      responseType: 'code',
      params: {
        scope: 'openid email' // Learn about scopes: https://auth0.com/docs/scopes
      }
    }
  });
</script>
<button onclick="lock.show();">Login</button>
<div id="root" style="width: 320px; margin: 40px auto; padding: 10px; border-style: dashed; border-width: 1px; box-sizing: border-box;">
    embedded area
</div>
<script src="https://cdn.auth0.com/js/lock/10.12.1/lock.min.js"></script>
<script>
  var lock = new Auth0Lock('YOUR_CLIENT_ID', 'YOUR_AUTH0_DOMAIN', {
    container: 'root',
    auth: {
      redirectUrl: 'https://YOUR_APP/callback',
      responseType: 'code',
      params: {
        scope: 'openid email' // Learn about scopes: https://auth0.com/docs/scopes
      }
    }
  });
  lock.show();
</script>
<script src="https://cdn.auth0.com/js/lock-passwordless-2.2.min.js"></script>
<script>
  var lock = new Auth0LockPasswordless('YOUR_CLIENT_ID', 'YOUR_AUTH0_DOMAIN');
  function open() {
    lock.sms({
      callbackURL: 'https://YOUR_APP/callback',
      authParams: {
        scope: 'openid email' // Learn about scopes: https://auth0.com/docs/scopes
      }
    });
  }
</script>
<button onclick="window.open();">SMS</button>
<script src="https://cdn.auth0.com/js/lock-passwordless-2.2.min.js"></script>
<script>
  var lock = new Auth0LockPasswordless('YOUR_CLIENT_ID', 'YOUR_AUTH0_DOMAIN');
  function open() {
    lock.emailcode({
      callbackURL: 'https://YOUR_APP/callback',
      authParams: {
        scope: 'openid email'  // Learn about scopes: https://auth0.com/docs/scopes
      }
    });
  }
</script>
<button onclick="window.open();">Email Code</button>
<button class="signin-google">Sign in with Google (redirect)</button><br>
<button class="signin-google-popup">Sign in with Google (popup)</button><br>
<br><p>--- or ---</p>
<label>Email</label><input type="text" id="email"><br>
<label>Password</label><input type="password" id="password"><br>
<button class="signin-db">Sign in with Email/Password</button>

<script src="https://cdn.auth0.com/w2/auth0-7.1.min.js"></script>
<script src="http://code.jquery.com/jquery.js"></script>
<script>
  var auth0 = new Auth0({
    domain:         'YOUR_AUTH0_DOMAIN',
    clientID:       'YOUR_CLIENT_ID',
    callbackURL:    'https://YOUR_APP/callback'
  });
  // sign-in with social provider with plain redirect
  $('.signin-google').on('click', function() {
    auth0.signin({connection: 'google-oauth2'}); // use connection identifier
  });
  // sign-in with social provider using a popup (window.open)
  $('.signin-google-popup').on('click', function() {
    auth0.signin({popup: true, connection: 'google-oauth2'},
                function(err, profile, id_token, access_token, state) {
                    /*
                      store the profile and id_token in a cookie or local storage
                        $.cookie('profile', profile);
                        $.cookie('id_token', id_token);
                    */
                });
  });
  $('.signin-db').on('click', function() {
    auth0.signin({
      connection: 'foo',
      username: 'bar',
      password: 'foobar'
    },
    function (err, profile, id_token, access_token, state) {
      /*
          store the profile and id_token in a cookie or local storage
            $.cookie('profile', profile);
            $.cookie('id_token', id_token);
        */
    });
  });
</script>

NOTE: The callbackURL specified in the Auth0Lock constructor must match the one specified in the Allowed Callback URLs area in your Auth0 dashboard. Follow the introduction step for further detail.

If you wish to force an identity provider, you may redirect the user and specify the connection name in the query string.

redirect_to '/auth/oauth2?connection=CONNECTION_NAME'

Click here to check all the information that the userinfo hash has.

Check the User's Authentication Status

You can use a controller concern to control access to routes that require the user to be authenticated.

module Secured
  extend ActiveSupport::Concern

  included do
    before_action :logged_in_using_omniauth?
  end

  def logged_in_using_omniauth?
    redirect_to '/' unless session[:userinfo].present?
  end
end

Include the concern in the corresponding controller to prevent unauthenticated users from accessing its routes:

class DashboardController < ApplicationController
 include Secured

  def show
  end
end

Display Error Descriptions

Configuration the application to display erros by adding the following to config/environments/production.rb:

OmniAuth.config.on_failure = Proc.new { |env|
  message_key = env['omniauth.error.type']
  error_description = Rack::Utils.escape(env['omniauth.error'].error_reason)
  new_path = "#{env['SCRIPT_NAME']}#{OmniAuth.config.path_prefix}/failure?message=#{message_key}&error_description=#{error_description}"
  Rack::Response.new(['302 Moved'], 302, 'Location' => new_path).finish
}

Troubleshooting

ActionDispatch::Cookies::CookieOverflow

This error means that a cookie session is being used and because the whole profile is being stored, it overflows the max-size of 4 kb. If you are unable to access the user profile and you get an error similar to NoMethodError, undefined method '[]' for nil:NilClass, try using In-Memory store for development.

Go to /config/initializers/session_store.rb and add the following:

Rails.application.config.session_store :cache_store

Go to /config/enviroments/development.rb and add the following:

config.cachestore = :memorystore

It is recommended that a memory store such as MemCached being used for production applications.

SSL Issues

Under some configurations, Ruby may not be able to find certification authority certificates (CA certs).

Download the CA certs bundle to the project directory:

$ curl -o lib/ca-bundle.crt http://curl.haxx.se/ca/ca-bundle.crt

Add this initializer to config/initializers/fix_ssl.rb:

require 'open-uri'
require 'net/https'

module Net
  class HTTP
    alias_method :original_use_ssl=, :use_ssl=

    def use_ssl=(flag)
      path = ( Rails.env == "development") ? "lib/ca-bundle.crt" : "/usr/lib/ssl/certs/ca-certificates.crt"
      self.ca_file = Rails.root.join(path).to_s
      self.verify_mode = OpenSSL::SSL::VERIFY_PEER
      self.original_use_ssl = flag
    end
  end
end

"failure message=invalid_credentials"

This issue doesn't occur when working locally but may happen in a staging or production environment. The error message may be displayed as:

omniauth: (auth0) Authentication failure! invalid_credentials: OAuth2::Error, server_error: The redirect URI is wrong. You send [wrong url], and we expected [callback url set in your app settings]

To solve this, add the following to config/environments/staging.rb or production.rb:

OmniAuth.config.full_host = "http://www.example.com"

Substitute http://www.example.com with the actual URL you'll be using in your application.

Previous Tutorial
1. Introduction
Next Tutorial
3. Custom Login
Use Auth0 for FREECreate free Account