login facebook, twitter sử dụng sorcery và những điều cần lưu ý
This post hasn't been updated for 7 years
1. Giới thiệu
Tương tự như người anh em devise, sorcery là công cụ khá mạnh dùng trong authentication. Nhưng nó đơn giản hơn devise từ tính năng cho đến document .
Vì config của nó khá đơn giản, nếu chỉ dùng với các chức năng cơ bản thì như vậy đã là đủ, nhưng muốn mở rộng, phức tạp hơn một chút thì chúng ta phải customize nó một cách phức tạp hơn(vì nó không phổ biến như devise nên những vấn đề liên quan thì khó tìm kiếm hơn).
Login facebook, twitter là chức năng được sử dụng khá phổ biến trong các website hiện nay, và trong bài viết này mình sẽ hướng dẫn về việc tạo chức năng này bằng gem sorcery.
2. Config gem
- Việc đầu tiên khi sử dụng gem vẫn là add gem file và bundle. Lưu ý sử dụng phiên bản gem từ 0.11 trở lên
gem 'sorcery', '~> 0.11.0'
-
Việc config app facebook, và twitter các bạn có thể tham khảo https://auth0.com/docs/connections/social/facebook https://developer.twitter.com/en/docs/basics/authentication/overview/application-permission-model
-
chức năng login facebook, twitter ứng với external của gem, và mình sẽ nói qua đó là:
run:
rails g sorcery:install external --only-submodules
lệnh này sẽ tạo ra table
class SorceryExternal < ActiveRecord::Migration
def change
create_table :authentications do |t|
t.integer :user_id, :null => false
t.string :provider, :uid, :null => false
t.timestamps
end
end
end
Tức là giờ đây, table user của bạn sẽ liên kết 1 nhiều với bảng authentications (đối với những thanh niên login bằng facebook thì sẽ tạo ra thêm 1 record lưu uid, provider
rake db:migrate
tạo model authentication
rails g model Authentication --migration=false
Tiếp theo là đến file config của socery: Nguyên văn trong document sẽ là:
# config/initializers/sorcery.rb
Rails.application.config.sorcery.submodules = [:external, blabla, blablu, ...]
Rails.application.config.sorcery.configure do |config|
...
config.external_providers = [:twitter, :facebook]
#add this file to .gitignore BEFORE putting any secret keys in here, or use a system like Figaro to abstract it!!!
config.twitter.key = "<your key here>"
config.twitter.secret = "<your key here>"
config.twitter.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=twitter"
config.twitter.user_info_mapping = {:username => "screen_name"}
config.facebook.key = "<your key here>"
config.facebook.secret = "<your key here>"
config.facebook.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=facebook"
config.facebook.user_info_mapping = {:email => "email", :name => "name", :username => "username", :hometown => "hometown/name"} #etc
config.facebook.scope = "email,offline_access,user_hometown,user_interests,user_likes" #etc
config.facebook.display = "popup"
...
# --- user config ---
config.user_config do |user|
...
# -- external --
user.authentications_class = Authentication
...
end
...
end
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :authentications_attributes
authenticates_with_sorcery! do |config|
config.authentications_class = Authentication
end
has_many :authentications, :dependent => :destroy
accepts_nested_attributes_for :authentications
end
# app/models/authentication.rb
class Authentication < ActiveRecord::Base
attr_accessible :user_id, :provider, :uid
belongs_to :user
end
# app/controllers/oauths_controller.rb
class OauthsController < ApplicationController
skip_before_filter :require_login
# sends the user on a trip to the provider,
# and after authorizing there back to the callback url.
def oauth
login_at(params[:provider])
end
def callback
provider = params[:provider]
if @user = login_from(provider)
redirect_to root_path, :notice => "Logged in from #{provider.titleize}!"
else
begin
@user = create_from(provider)
# NOTE: this is the place to add '@user.activate!' if you are using user_activation submodule
reset_session # protect from session fixation attack
auto_login(@user)
redirect_to root_path, :notice => "Logged in from #{provider.titleize}!"
rescue
redirect_to root_path, :alert => "Failed to login from #{provider.titleize}!"
end
end
end
#example for Rails 4: add private method below and use "auth_params[:provider]" in place of
#"params[:provider] above.
# private
# def auth_params
# params.permit(:code, :provider)
# end
end
- Để hợp thời, chúng ta không sử dụng attr_accessible mà sẽ sử dụng strong params trong controller và viết lại như sau
module NowApi
class OauthsController < ApiBaseController
skip_before_action :require_valid_token
def oauth
login_at(params[:provider])
end
def callback
provider = auth_params[:provider]
...
end
end
private
def auth_params
params.permit(:code, :provider, authentications_attributes: [:uid, :provider])
end
end
Hiểu đơn giản thì khi tạo ra một user tương ứng với user facebook, sẽ tạo ra 1 record authentications tương ứng sử dụng nested_attributes
Nhưng một điều khá buồn, là nếu chỉ dùng nguyên config sorcery.rb này thì bạn sẽ không thể lấy được email, user_name bởi vì sorcery bắt bạn phải tự config tức là bạn cần thông tin gì từ user facebook, bạn phải include nó theo API hướng dẫn của facebook https://developers.facebook.com/tools/explorer/ bạn muốn lấy email, name, thậm chí là avatar thì bạn cần thêm dòng lệnh include như sau
config.facebook.user_info_path = "me?fields=email,name,picture.width(400).height(400),gender"
tại sao user_info_path lại là "me", cái này lấy ở đâu thì chỉ cần search code của gem tại lib/sorcery/providers/facebook.rb Nhờ include params như thế này mà chúng ta có thể lấy email, name, avatar từ facebook
Tương tự như vậy đối với twitter thì sẽ thêm config
config.twitter.user_info_path = '/1.1/account/verify_credentials.json?include_email=true'
tham khảo thêm về include trong api của twitter https://developer.twitter.com/en/docs/accounts-and-users/manage-account-settings/api-reference/get-account-verify_credentials
3. Kết luận
Việc login bằng facebook hay twitter trên socery khá là đơn giản, có thể là đơn giản hơn cả devise. Nhưng mức độ phổ biến của nó thì it hơn bởi vậy gặp những lỗi về config thì chúng ta sẽ khó tim được những câu hỏi và support tương tự. Điều lưu ý trên tuy không có gì ghê gớm, nhưng do document không ghi đầy đủ về việc include như thế nào nên sẽ rất khó khăn khi mới tiếp xúc sorcery. Chúc các bạn sử dụng gem thành công
4. Tài liệu tham khảo
https://github.com/Sorcery/sorcery https://github.com/NoamB/sorcery/wiki/External https://developer.twitter.com/en/docs/basics/authentication/overview/application-permission-model https://developers.facebook.com/tools/explorer/
All Rights Reserved