Posts tagged Omniauth

Dennis

Sign-in using GitHub

Dennis | about 1 year ago | Rails Omniauth

I want a painless way for visitors of my website to post comments, without going through the hassle of registering for an account. OmniAuth is a gem that allows us to do just that. In this tutorial I'll show how to enable users to sign-in/up using github and OAuth.

The setup

We're going to configure omniauth to use github for a users credentials. We do however create a user for the authenticated user, which will contain the information we obtain from GitHub. For one, this makes sure that our app doesn't crash and burn when GitHub is offline.

We're going to assume that you already have an authentication scheme working. With a sign_in(user) method that performs all the work related to sessions, etc. because I just want to show the implementation of omniauth itself.

OmniAuth has several strategies. You can install these separately in your Gemfile. Here we'll use the omniauth-github gem. Look at the OmniAuth wiki for more strategies.

# Gemfile
gem 'omniauth-github'

# Bash
bundle install

With omniauth-github installed, we have to setup the authentication. OmniAuth is provided as a rack middleware application and we include it in our app using an initializer. Create a new file in the config/initializers path and give it a descriptive name (e.g. omniauth.rb). The initializer does the setup of the authentication strategy for github. It needs a github-key and github-secret. You can obtain these at GitHub. For development purposes you can set the application url to http://localhost:3000 and the callback url to http://localhost:3000/auth/github/callback.

# config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :github, APP_CONFIG[:github_id], APP_CONFIG[:github_secret]
end

As you see above, I store my variables in a global has called APP_CONFIG. You can also just put the key and secret right in the initializer. However, whatever you do, do not make the secret public, they don't call it a secret for nothing.

Believe it or not, but with this minimal configuration, the first part of the authentication is already working. Fire up your rails server and go to /auth/github. The application will forward you to GitHub, where it will ask you if you want to allow it to have access.

The next step from github will be to make a call back to your application. If everything went right, it should call to /auth/github/callback, if a user rejects your application, it will call /auth/failure. Let's catch those calls and do something with it.

When we reach /auth/failure, we could for example want to redirect the user to the root path and display an error message saying that the authentication failed.

A successful callback will provide you with the users information in request.env["omniauth.auth"], You could use pry or the rails logger to inspect this object.

# config/routes.rb
match "/auth/:provider/callback" => "sessions#create_from_github"
match "/auth/failure" => "sessions#failure_from_github"

# app/controllers/sessions_controller.rb
def create_from_github
   # Rails.logger.debug request.env["omniauth.auth"]
end

def failure_from_github
  flash[:error] = "Error logging in with GitHub. #{params[:message]}"
  redirect_to root_path
end

What we really want to do with the information, however, is two things.

  1. Check if the user is new. If so, create a new User model for this user.
  2. Find the User model that belongs to this user and log him in.

We can do this using the uid that is provided by GitHub in the returned omniauth.hash. Lets add this column to our user model.

rails generate migration add_github_uid_to_users github_uid:string

Now, in our SessionsController, we can check if a user with this ID already exists by searching the user database for that GitHub UID. If it doesn't find one, we can create a new user using the information provided by GitHub.

# SessionsController
def create_from_github
  omniauth = request.env["omniauth.auth"]
  @user = User.find_by_github_uid(omniauth["uid"]) || User.create_from_omniauth(omniauth)
  # Hook into your own authentication system!
  sign_in @user 
  # This would normally be configured to return to the previous path 
  redirect root_path, :notice => "Welcome, #{@user.name}"  
end

Creating the user is done using the createfromomniauth method:

#user.rb
def self.create_from_omniauth(omniauth)
  User.new.tap do |user|
    user.github_uid = omniauth["uid"]
    user.name = omniauth["info"]["nickname"]
    user.email = omniauth["info"]["email"]
    user.save!
  end
end

Now, when a user tries to login using omniauth, your call will create a user if necessary, log him user in and redirect him to the root path. Great, isn't it? But, how does a user login using omniauth? Simple, just redirect him to /auth/github and omniauth will take care of everything else.

Notes:

I found that OAuth is picky about domain names. So, www.web-l.nl and web-l.nl are two different worlds. So, if you setup your application to use example.com, make sure that all requests at www.example.com get redirected to the preferred domain name before making the OAuth call.

In my case, I use Apache and mod_rewrite. In the virtualhost block I rewrite the url to use the root domain name.

RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^(.*)$ http://example.com$1 [R=301,L]
Back to index