Note: This document is a work in progress. You can help improve it.

Adding OAuth authentication to our application using auth0

NOTE This assumes you created an API only style Rails application

NOTE This also assumes you have React Router installed in your React client

Configuring auth0 service

  • Login (or signup)
  • You will be asked to choose a domain name
  • Create a new application
  • Choose an application name
  • Select Single Page Web App as the application type
  • Select the settings tab. Scroll down to Allowed Callback URLs and enter http://localhost:3001/callback
  • Make note of your domain and your Client ID values

React Front End

In this section we will configure our React front end to

  • integrate with the Auth0 interface
  • Add routes to handle the login, logout, and callback from Auth0

Add auth0 to our client application

Inside the client folder (cd client)

yarn add auth0-js

NOTE Restart your yarn start if it was already running

Create a class to interact with the Auth0 API

  • In the React app create an Auth class in src/auth.js

NOTE Replace the value of DOMAIN with the domain from your auth0 account NOTE Replace the value of CLIENTID with the configured client id from your auth0 account NOTE Replace the value of AFTER_LOGIN with the React route to go to after a successful login NOTE Replace the value of FAILED_LOGIN with the React route to go to after a failed login NOTE Replace the value of AFTER_LOGOUT with the React route to go to after a successful logout

import auth0 from 'auth0-js'
import history from './history'

const DOMAIN = ''
const CLIENTID = 'xxxxxxxxx'
const AFTER_LOGIN = '/'
const AFTER_LOGOUT = '/'

class Auth {

  auth0 = new auth0.WebAuth({
    domain: DOMAIN,
    clientID: CLIENTID,
    redirectUri: `${window.location.protocol}//${}/callback`,
    responseType: 'token id_token',
    scope: 'openid email profile'

  constructor() {
    this.login = this.login.bind(this)
    this.logout = this.logout.bind(this)
    this.handleAuthentication = this.handleAuthentication.bind(this)
    this.isAuthenticated = this.isAuthenticated.bind(this)

  login() {

  logout() {
    // Clear Access Token and ID Token from local storage

    window.location = AFTER_LOGOUT

  handleAuthentication(callback) {
    this.auth0.parseHash((err, authResult) => {
      if (authResult && authResult.accessToken && authResult.idToken) {

        if (callback) {

        window.location = AFTER_LOGIN
      } else if (err) {
        window.location = FAILED_LOGIN

  setSession(authResult) {
    // Set the time that the Access Token will expire at
    let expiresAt = JSON.stringify(
      authResult.expiresIn * 1000 + new Date().getTime()
    localStorage.setItem('access_token', authResult.accessToken)
    localStorage.setItem('id_token', authResult.idToken)
    localStorage.setItem('expires_at', expiresAt)

  isAuthenticated() {
    // Check whether the current time is past the
    // Access Token's expiry time
    let expiresAt = JSON.parse(localStorage.getItem('expires_at'))
    return new Date().getTime() < expiresAt

  getIdToken() {
    const idToken = localStorage.getItem('id_token')
    if (!idToken) {
      throw new Error('No ID Token found')
    return idToken

  getAccessToken() {
    const accessToken = localStorage.getItem('access_token')
    if (!accessToken) {
      throw new Error('No Access Token found')
    return accessToken

  getProfile(cb) {
    let accessToken = this.getAccessToken()
    this.auth0.client.userInfo(accessToken, (err, profile) => {
      if (profile) {
        this.userProfile = profile
      cb(err, profile)

  authorizationHeader() {
    return `Bearer ${this.getIdToken()}`

const auth = new Auth()

export default auth
  • Create src/history.js
import createHistory from 'history/createBrowserHistory'

export default createHistory()

Add Routes to our <Router> to work with login, logout, and callback

  • In the component that contains your Router, add the following
import auth from './auth'

import history from './history'

We need the auth component so we can allow the user to login, logout, and access profile information. The history object will allow us to provide history to our Router, and to modify it as necessary.

Because of that we will change from using the BrowserRouter to a simple Router and provide our custom history object to it.

  • In your import, change your BrowserRouter to a simple Router (e.g. BrowserRouter as Router to just Router)

  • In your render function where <Router> is found, change this to <Router history={history}>

  • To your router add these routes

  <Route path="/login" render={() => auth.login()} />
    render={() => {

      return <></>
  <Route path="/callback" render={() => {
      auth.handleAuthentication(() => {
        // NOTE: Uncomment the following lines if you are using axios
        //       to set the axios authentication headers

        // axios.defaults.headers.common = {
        //   Authorization: auth.authorizationHeader()
        // }

      return <></>

Now we can use the /login and /logout routes to allow the user to login or logout. Additionally we can check if the user is authenticated and redirect them to the /login page if we need to (e.g. ensuring that some parts of our app are protected behind a login form)

Setup axios to provide custom Authorization headers for every request.

To do this, add this code in your App componentWillMount callback. It will ensure every axios request will include the authorization header to let our backend know our identity.

componentWillMount() {
  if (auth.isAuthenticated()) {
    axios.defaults.headers.common = {
      Authorization: auth.authorizationHeader()

NOTE If you are using fetch, or if you do not want to set the common headers, you must provide the Authorization header on every request.

Rails Backend Setup

Setting up the Rails environment to be ready to process our requests

Before we get started we need to add the following gem to our Gemfile:

bundle add jwt

NOTE You must then restart the Rails application

Add a way to find a current user

In our Rails application we need a way to access the currently active user. We will do this by adding a private method to ApplicationController that fetches the currently authenticated user.

NOTE In the instructions below if you called your User model Profile or such, use that class name in place of User

In the file app/controllers/application_controller.rb


  def current_user
    @user ||= begin
      token = request.headers["Authorization"].to_s.split(" ").last
      payload, header = *JSONWebToken.verify(token)

  rescue JWT::VerificationError, JWT::DecodeError

If you don't already have a User model in your app ...

If you have not created a model for the user yet, you should do so now.

You should include the user's name, and their auth_sub. The auth_sub is the OAuth identifier for the user. It includes the service (google, facebook, twitter) and their identifier on that service. It is the column we lookup users by and is mandatory.

You may also want to add other columns such as email, etc.

NOTE If you already have a User model, do not do this step.

rails generate model User name:string email:string auth_sub:string

If you already have a User model ...

We need to add the auth_sub column on our User (or Profile or etc) model:

rails generate migration AddAuthSubToUser auth_sub:string

Add a method to the User (Profile, etc) class to find or create the account

This code needs our User class to be able to find (or create) a user. So, in your User ActiveRecord class add the following code. NOTE that inside the do block is where you would capture any user specific information such as avatar, name, email, etc.

# Add this code *within* the `class` definition
def self.from_auth_hash(payload)
  User.find_or_create_by(auth_sub: payload["sub"]) do |user|
    # This code will be called whenever we create a User for the first time.

    # This code would save a user's avatar as a URL
    # user.avatar_url = payload["picture"]

    # This code would attach an ActiveStorage profile image by downloading the user's profile and storing it locally
    # If you do this, you must also run `bundle add down` to add the `down` gem
    # begin
    #   picture =["picture"])
    #   user.profile_image.attach(io: picture, filename: picture.original_filename)
    # rescue Down::Error => exception
    # exception
    # end

    # This code would store their email address
    # = payload["email"]

    # Capture the user's name = payload["name"]

Payload example

Here is an example of what is in the payload variable.

  "given_name"     => "Gavin",
  "family_name"    => "Stark",
  "nickname"       => "gavin",
  "name"           => "Gavin Stark",
  "picture"        => "",
  "gender"         => "male",
  "locale"         => "en",
  "updated_at"     => "2019-01-14T15:37:55.567Z",
  "email"          => "",
  "email_verified" => true,
  "iss"            => "",
  "sub"            => "google-oauth2|113743542470462017512",
  "aud"            => "sPmodN6xIAdBYJuCZczXxKXqf0Bwht81",
  "iat"            => 1547480275,
  "exp"            => 1547516275,
  "at_hash"        => "jivTOKfCQHcnVtdTwo0qUA",
  "nonce"          => "fYMdfd_HsH2FMQjBilfGyCX0LP_Y7vPn"

Add the JWT support code

Next we need a class, JSONWebToken in the file app/lib/json_web_token.rb that can parse the jwt and present a payload and header

NOTE Replace the two instances of the text OURDOMAIN with the domain name you created in auth0

require "net/http"
require "uri"

class JSONWebToken
  def self.verify(token)
    JWT.decode(token, nil,
              algorithm: "RS256",
              iss: "",
              verify_iss: true) do |header|

  def self.jwks_hash
    jwks_raw = Net::HTTP.get URI("")

    jwks_keys = Array(JSON.parse(jwks_raw)["keys"]) { |key| [key["kid"],["x5c"].first)).public_key] }.to_h