🚀
Rails 6 + mongoid + DeviseでのTwitterアカウントによるSSO
この記事では書かない前提
参考文献
その他、多数の記事を参考にさせていただきました。
概要
使用ライブラリ
- devise
- omniauth
- omniauth-twitter
仕様
- Twitterアカウントでしかログインできない前提にする
- DeviseはEmailとPasswordログインを前提としているのでそれらのフィールドは消す
RailsにDeviseを導入する
- Gemfileに以下を追記する
gem 'devise'
gem 'omniauth', '1.9.1'
gem 'omniauth-twitter'
omniauthを1.9.1にしないとハマるという情報があったのでバージョン指定しているけど最新バージョンで直っているかも知れない
-
bundle install
する
DeviseでUserモデルを初期化する
- 以下のコマンドを実行
rails g devise:install
rails g devise User
DeviseはActiveRecordじゃなくてmongoidが入っている場合はちゃんとそれを理解したコードを生成してくれる。素晴らしい。あとで全部消すけど。
重要
-
rails g devise:view
を してはいけない- Email, PasswordでサインアップやサインインするためのViewが生成されてしまうが不要なため
omniauth-twitterの導入
Users::OmniauthCallbacksControllerをつくる
- 以下のコマンドを実行
rails g controller users/omniauth_callbacks
rails routes
- 以下を確認
-
app/controllers/users/omniauth_callbacks_controller.rb
が生成されたこと -
user_twitter_omniauth_callback
pathが上記のコントローラーになっていること
-
Users::OmniauthCallbacksControllerを実装する
-
app/controllers/users/omniauth_callbacks_controller.rb
をまるっと以下のように書き換える
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def twitter
callback_from :twitter
end
private
def callback_from(provider)
provider = provider.to_s
@user = User.twitter_oauth(request.env['omniauth.auth'])
if @user.persisted?
flash[:notice] = I18n.t('devise.omniauth_callbacks.success', kind: provider.capitalize)
sign_in_and_redirect @user, event: :authentication
else
session["devise.#{provider}_data"] = request.env['omniauth.auth']
redirect_to root_path
end
end
end
Deviseでomniauth-twitterを使うように設定
config/initializers/devise.rb
に以下を追記
config.omniauth :twitter, ENV['TWITTER_TOKEN'], ENV['TWITTER_SECRET'], oauth_callback: "#{ENV['ORIGIN']}#{user_twitter_omniauth_callback_path}"
OmniAuth.config.logger = Rails.logger if Rails.env.development?
config/routes.rb
を以下のように変更
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
.env
ファイルを設置する
ORIGIN=http://localhost:3000/
TWITTER_TOKEN=https://dev.twitter.com/で確認できるやつ
TWITTER_SECRET=https://dev.twitter.com/で確認できるやつ
Userモデルにフィールドとメソッドを追加
ポイント
-
rails g devise User
で生成されたapp/models/user.rb
の内容をまるっと書き換える- TwitterでのSSOにしたいので、Email, Passwordは不要にしたい
- しかしDeviseの
:database_authenticatable
はEmail, Passwordが必須-
authentication_keys: [:uid]
を指定しself.find_for_database_authentication
を実装する -
field :twitter_token, as: :encrypted_password
としてDevise(が内部で使っているWarden)がencrypted_password
attributeにアクセスできるようにしてやる
-
- mongoidなのでマイグレーションは不要
class User
include Mongoid::Document
include Mongoid::Timestamps
devise :database_authenticatable, :rememberable, :omniauthable, authentication_keys: [:uid]
## Rememberable
field :remember_created_at, type: Time
# OmniAuthable
field :provider, type: String
field :uid, type: String
# Twitter
field :twitter_id, type: String, default: ""
field :twitter_name, type: String, default: ""
field :twitter_icon, type: String, default: ""
field :twitter_token, as: :encrypted_password, type: String, default: ""
field :twitter_secret, type: String, default: ""
def self.twitter_oauth(auth)
user = User.where(uid: auth.uid, provider: auth.provider).first
if user
user.update(
twitter_id: auth.info.nickname,
twitter_name: auth.info.name,
twitter_icon: auth.info.image,
twitter_token: auth.credentials.token,
twitter_secret: auth.credentials.secret,
)
else
user = User.create(
provider: auth.provider,
uid: auth.uid,
twitter_id: auth.info.nickname,
twitter_name: auth.info.name,
twitter_icon: auth.info.image,
twitter_token: auth.credentials.token,
twitter_secret: auth.credentials.secret,
)
end
user
end
def self.find_for_database_authentication(warden_conditions)
uid = warden_conditions[:uid].to_s.downcase.strip
find_by(uid: uid)
end
end
ログイン画面を作る
以下のコマンドを実行
rails g controller Root index
app/views/root/index.html.erb
を以下のように書き換える
<% if user_signed_in? %>
<div>
<%= link_to 'Sign out', destroy_user_session_path, method: :delete %>
</div>
<h2>
Twitter account:
@<%= current_user.twitter_id %>
(<%= current_user.twitter_name %>)
<%= image_tag current_user.twitter_icon %>
</h2>
<% else %>
<%= link_to 'Sign In with Twitter', user_twitter_omniauth_authorize_path, method: :post %>
<% end %>
config/routes.rb
に以下を追加
root to: 'root#index'
以上です。
動作確認
docker compose up
して
にアクセスする。
Sign In with Twitter
をクリックする
こうなる
Discussion