Introduction

In this article we will show you how to implement Github authentication in your Ruby on Rails application. This allows your users to seamlessly sign on to your application without you having to store sensitive information such as passwords. Let's get started.

Github Setup

Before we can start building our Rails application, we must create an oauth2 application under Github. Doing so is easy, simply follow the steps listed below.

  1. First, visit https://github.com/settings/developers. You should see the screen listed below.

    Github Oauth2 Applications

  2. Next, click either of the buttons that say Register a new application. The screen below should appear.

    Github New Oauth2 Application

  3. Fill in your application details . For Authorization callback URL use http://localhost:3000/auth/github/callback. When you click Register Application you will be taken to the screen below. Make note of the Client ID and Client Secret. (not the ones in the screenshot, those are for example only and are not valid). You will use those when building your Rails application.

    Github Application Details

Great! Now we are ready to build our Rails application.

Rails Application Setup

The first thing we need to do is add the omniauth-github gem to our gemfile. This gem implements the oauth2 protocol for single sign on to github. Open your Gemfile now and add in the code listed below.

Gemfile:

gem 'omniauth-github'

Great, Now run a bundle install to install the gem.

Terminal Commands:

bundle install

Now we need to add an initializer called omniauth.rb. This initializer will initialize the Omniauth Github provider. Create a file called omniauth.rb in your config/initializers folder and add in the code listed below.

config/initializers/omniauth.rb:

opts = { scope: 'user:email' }

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :github, Rails.application.secrets.github_client_id, Rails.application.secrets.github_client_secret, opts
end

Now let's add our client ID and secret we took note of earlier to the config/secrets.yml file. Before you do this, you should add config/secrets.yml to your .gitignore file to ensure you don't accidentally commit it to git. Open up the secrets.yml file and modify it so it looks like the code listed below, replacing the example values with your client id and secret.

config/secrets.yml:

development:
  secret_key_base: e839e2cc3c9e9977b5765bd3dfcabcc944c33a4d5776a58d32b2150e760b22a9649eeba7dd25e7aff50d76218b66bb48e98823e66b6732e0f6546caf34bec27a
  github_client_id: Your Github client id
  github_client_secret: Your Github client secret

test:
  secret_key_base: 8773c1ca845f1512dcbb6fdc2956eb1b87b053c563a3279dfaf74e82ed1cd481ee3949cbd4f1ebda087fffa4fafa98315507f1e4dfb9b1619d30fd52cf8a7e3c

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

You'll notice that we didn't fill out values for test and production. This is because Github only supports 1 callback URL, so we must create a new application for each environment.

Great, now let's create a model called User. This model will store the fields github provides us, and will also contain the business logic for signing in. Run the command below to create the user model now.

Terminal Commands:

rails g model User username avatar_url email uid provider oauth_token

Next run a rake db:migrate to migrate the database.

Terminal Commands:

rake db:migrate

Next, create a new controller called SessionsController. This controller will handle logging in and logging out. Run the command below to create this controller now.

Terminal Commands:

rails g controller sessions new create destroy

Now we need to update our routes file to add in omniauth specific routes along with a root path. Open up your config/routes.rb file now and modify it so that it looks like the code listed below.

config/routes.rb:

Rails.application.routes.draw do
  get "/auth/:provider/callback", to: "sessions#create"
  get 'auth/failure', to: redirect('/')
  delete 'signout', to: 'sessions#destroy', as: 'signout'
  root to: 'sessions#new'
end

Next we need to add some code to our user model to tell our rails app how to parse the data that omniauth provides. Open up your app/models/user.rb file and modify it so it looks like the code listed below.

app/models/user.rb:

class User < ActiveRecord::Base
  def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
      user.email = auth.info.email
      user.uid = auth.uid
      user.provider = auth.provider
      user.avatar_url = auth.info.image
      user.username = auth.info.name
      user.oauth_token = auth.credentials.token
      user.save!
    end
  end
end

The self.from_omniauth method above will attempt to find the given user based on the provider and uid. If it can't find a user matching that information, it will create a new entry. All fields are updated with the latest information and the user is saved.

Great, now we need to add some code to our Sessions controller. Open up the app/controllers/sessions_controller.rb file now and add in the code listed below.

app/controllers/sessions_controller.rb:

class SessionsController < ApplicationController
  def new
  end

  def create
    user = User.from_omniauth(env["omniauth.auth"])
    
    if user.valid?
      session[:user_id] = user.id
      redirect_to request.env['omniauth.origin']
    end
  end

  def destroy
    reset_session
    redirect_to request.referer
  end
end

Great, now we need to create a method called current_user. The current user method will return the currently logged in user or nil if the user isn't logged in. We will create this method in our Application controller and share it with our views by declaring it to be a helper_method. Open up your app/controllers/application_controller.rb file and modify it so that it looks like the code listed below.

app/controllers/application_controller.rb:

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  helper_method :current_user

  def current_user
    session[:user_id].nil? ? nil : User.find(session[:user_id])
  end
end

Next we need to add some code to our New view for our Sessions controller. Open up the app/views/sessions/new.html.erb file and modify it so that it looks like the code listed below.

app/views/sessions/new.html.erb:

<% if current_user.blank? %>
  <h1>Please Sign In</h1>
  <%= link_to 'Sign In with Github', '/auth/github' %>
<% else %>
  <p>
    You are signed in as <%= current_user.username %>.  Click the button below to sign out.
  </p>

  <%= button_to "Sign Out", signout_path, method: :delete %>
<% end %>

Now if you start a rails server and open up chrome to your Rails development server you'll see that you can sign in and out of Github. That's it! That's all there is to it!

As always, if you have any questions, comments, or feedback, please feel free to leave a comment below. If you want to support us, please consider purchasing a Pro membership as it helps us continue to provide high quality content. Thanks for reading!