Omniauth
####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