####Gemfile Omniauth-identity 는 hash function으로 bcrypt를 이용합니다. Gemfile에 bcrypt도 추가해줍니다.

gem 'omniauth'
gem 'omniauth-identity'
gem 'omniauth-kakao'
gem 'bcrypt',         '3.1.11'

Omniauth 초기화

config/initializers/omniauth.rb TODO: Rack middleware가 뭐지? Omniauth가 좋은점이 authentication 이후에 그 정보를 어떻게 저장하고 보여주는지는 온전히 사용하는 사람의 몫으로 남겨져 있음.

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :kakao, ENV['KAKAO_CLIENT_ID']
  provider :identity, on_failed_registration: lambda { |env|    
    SessionsController.action(:new).call(env)
  }
  
  # Enable to redirect a failure page in development mode
  OmniAuth.config.on_failure = Proc.new { |env|
    OmniAuth::FailureEndpoint.new(env).redirect_to_failure
  }

  # 또는 Redirect Path를 설정하고 싶다면(or if you want to customize your Redirect Path)
  # provider :kakao, ENV['KAKAO_CLIENT_ID'], {:redirect_path => ENV['REDIRECT_PATH']}
end

####[Before] users table schema has_secure_password 를 이용해서 직접 User 모델에 authentication을 구현했었습니다.

create_table "users", force: :cascade do |t|
  t.string   "name"
  t.boolean  "admin",           default: false
  t.datetime "created_at",                      null: false
  t.datetime "updated_at",                      null: false
  t.string   "email"
  t.string   "password_digest"
end

####Create Identity Model & Migration User 모델에서 authentication을 제거하고, 이메일을 이용한 Authentication을 위해 Identity 모델을 만듭니다.

class RemoveEmailAndPasswordDigestFromUsers < ActiveRecord::Migration
  def change
    remove_column :users, :email
    remove_column :users, :password_digest
  end
end

class AddProviderAndUidToUsers < ActiveRecord::Migration
  def change
    add_column :users, :provider, :string
    add_index :users, :provider
    add_column :users, :uid, :integer
    add_index :users, :uid
  end
end

$ rails g model identity name:string email:string password_digest:string

app/controllers/sessions_controller.rb

def create
  auth = env["omniauth.auth"]
  user = User.find_or_create_from_omniauth(auth)
  log_in(user)
  
  if admin_user?
    redirect_to admin_path
  else
    redirect_to root_path
  end
end

def destroy
  log_out
  redirect_to root_url
end

def failure
  flash[:danger] = "로그인에 실패했습니다. 다시 시도해 주세요."
  redirect_to login_url
end

sessions_helper.rb

def log_in(user)
  session[:user_id] = user.id
end
def log_out
  session.delete(:user_id)
  @current_user = nil
end

app/models/user.rb 기존에 User 모델이 가진 validation을 지워줍니다. validation은 Identity 모델에 적용시킵니다.

class User < ActiveRecord::Base
  def self.find_or_create_from_omniauth(auth)
    find_by_provider_and_uid(auth["provider"], auth["uid"]) ||  create_with_omniauth(auth)
  end

  def self.create_with_omniauth(auth)
    create! do |user|
      user.provider = auth["provider"]
      user.uid = auth["uid"]
      user.name = auth["info"]["name"]
    end
  end

app/models/identities.rb

class Identity < OmniAuth::Identity::Models::ActiveRecord
  before_save { self.email = email.downcase }
  validates :name, presence: true
  validates :email, presence: true, uniqueness: {case_sensitive: false}
end

route.rb

get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
post "/auth/:provider/callback" => "sessions#create"  
get "/auth/:provider/callback" => "sessions#create"
get "/auth/failure" => "sessions#failure"
delete 'logout' => 'sessions#destroy'
resources :identities

app/views/sessions/new.html.erb

<%= form_tag "/auth/identity/callback" do %>
  <div class="form-group">
    <%= label_tag :auth_key, "이메일" %>
    <%= email_field_tag :auth_key, nil, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= label_tag :password, "비밀번호" %>
    <%= password_field_tag :password, nil, class: 'form-control' %>
  </div>
  <%= submit_tag "로그인", class: "btn btn-default" %>
  <div class="pull-right">
    <%= link_to "이메일로 가입하기", new_identity_path() %>
  </div>
<% end %>

app/views/identities/new.html.erb

<%= form_tag "/auth/identity/register" do %>
  <% if @identity && @identity.errors.any? %>
    <%= render partial: 'shared/error_messages', locals: {object: @identity} %>
  <% end %>
  <div class="form-group">
    <%= label_tag :email, "이메일" %>
    <%= email_field_tag :email, @identity.try(:email), class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= label_tag :name, "이름" %>
    <%= text_field_tag :name, @identity.try(:name), class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= label_tag :password, "비밀번호" %>
    <%= password_field_tag :password, nil, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= label_tag :password_confirmation, "비밀번호 재입력" %>
    <%= password_field_tag :password_confirmation, nil, class: 'form-control' %>
  </div>
  <%= submit_tag "가입하기", class: "btn btn-default" %>
<% end %>

app/controllers/identities_controller.rb

class IdentitiesController < ApplicationController
  def new
    @identity = env['omniauth.identity']
  end
end

https://github.com/omniauth/omniauth/wiki/FAQ#omniauthfailureendpoint-does-not-redirect-in-development-mode

http://railscasts.com/episodes/304-omniauth-identity?view=asciicast