OAuth with Rails

09 Dec, 2018

Categories back end dev

Rails Social OAUTH Login

Setup

rails new <app name> --database=postgresql

Postgres

Using the custom multiple database postgres docker image:

docker run -id --name rails-db -e POSTGRES_MULTIPLE_DATABASES="railyard-dev","railyard-test","railyard-prod" -e POSTGRES_USER=<database user> -e POSTGRES_PASSWORD=<password> -p 5434:5432 db:latest

Rails

Config

Needs:

  • OAUTH apps created at Twitter or Github (or Google, or Facebook)
  • Stripe API key
  • Mailgun API key

Initial setup:

#/.env

GITHUB_APP_ID=
GITHUB_APP_SECRET=
TWITTER_APP_ID=
TWITTER_APP_SECRET=

MAILER_DOMAIN=
MAILER_USERNAME=
MAILER_PASSWORD=

PUBLISHABLE_KEY=
SECRET_KEY=

#Gemfile

gem 'devise'
gem 'omniauth-github'
gem 'omniauth-twitter'
gem 'dotenv'
gem 'activerecord-session_store'
gem 'mailgun-ruby'
gem 'stripe'
gem 'jquery-rails'
#/config/database.yml

default: &default
  adapter: postgresql
  encoding: unicode
  pool: 5
  timeout: 5000
  username: worker
  password: S0itg0es1
  host: 127.0.0.1
  port: 5434

development:
  <<: *default
  database: railyard-dev

test:
  <<: *default
  database: railyard-test

production:
  <<: *default
  database: railyard-prod

Devise

rails g devise:install

Migrations:

#add_providers
class UpdateUsers < ActiveRecord::Migration[5.2]
    def change
      add_column(:users, :provider, :string, limit: 50, null: false, default: '')
      add_column(:users, :uid, :string, limit: 500, null: false, default: '')
    end
end
#create_sessions
class CreateSessions < ActiveRecord::Migration[5.2]
    def change
      create_table :sessions do |t|
        t.string :session_id, null: false
        t.text :data
        t.timestamps
      end

      add_index :sessions, :session_id, unique: true
      add_index :sessions, :updated_at
    end
end
#add_social_fields
class AddSocialToUsers < ActiveRecord::Migration[5.2]
  def change
      change_table :users do |t|
        t.column :social_desc, :string, :default => "None"
        t.column :social_image, :string, :default => "None"
        t.column :social_location, :string, :defaut => "None"
        t.column :social_nickname, :string, :default => "None"
    end
  end
end

Setup:

#/config/initializers/devise.rb
config.omniauth :github, ENV['GITHUB_APP_ID'], ENV['GITHUB_APP_SECRET'], scope: 'user,public_repo'
config.omniauth :twitter, ENV['TWITTER_APP_ID'], ENV['TWITTER_APP_SECRET']

# Find and change these:
config.sign_out_via = :get
config.mailer_sender = '<mailgun email domain>'
config.parent_mailer = 'ActionMailer::Base'
#/config/initialisers/session_store.rb
Rails.application.config.session_store :active_record_store, key: '_devise-omniauth_session'

Models:

#/app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :confirmable, :trackable, :omniauthable, omniauth_providers: [:github, :twitter]

     def self.create_from_provider_data(provider_data)
       where(provider: provider_data.provider, uid: provider_data.uid).first_or_create do | user |
         user.email = provider_data.info.email
         user.password = Devise.friendly_token[0, 20]
         user.social_desc = provider_data.info.description
         user.social_image = provider_data.info.image
         user.social_location = provider_data.info.location
         user.social_nickname = provider_data.info.nickname
         user.skip_confirmation!
         user.save
       end
   end
end

Controllers:

rails generate controller users/omniauth
#/config/controllers/user/omniauth_controller.rb
class Users::OmniauthController < Devise::RegistrationsController
    # github callback
    def github
      @user = User.create_from_provider_data(request.env['omniauth.auth'])
      if @user.persisted?

          session[:info] = request.env['omniauth.auth']

        sign_in_and_redirect @user
        set_flash_message(:notice, :success, kind: 'Github') if is_navigational_format?
      else
        flash[:error] = 'There was a problem signing you in through Github. Please register or try signing in later.'
        redirect_to new_user_registration_url
      end
    end

    # twitter callback
    def twitter
      @user = User.create_from_provider_data(request.env['omniauth.auth'])
      if @user.persisted?

        session[:creds] = request.env['omniauth.auth']['extra']['access_token']

        sign_in_and_redirect @user
        set_flash_message(:notice, :success, kind: 'Twitter') if is_navigational_format?
      else
        flash[:error] = 'There was a problem signing you in through Twitter. Please register or try signing in later.'
        redirect_to new_user_registration_url
      end
    end

    def failure
      flash[:error] = 'There was a problem signing you in. Please register or try signing in later.'
      redirect_to new_user_registration_url
    end
end

Routes:

devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth' }


Back