deviseとomniauthで簡単認証
-
対象者
:Railsでアカウント認証を実装したい人 -
成果
:認証機能の実装
最初に
私自身Rails初学者なんで間違っている箇所があるかと思います。
コメントなどで知らせてもらえると幸いです。
とりあえずRuby on Rails Tutorialの第3章から第5章までを終わらせている体で話します。
ファイルはアプリケーションルートからで記載しています。
bundle execは追い出しています。コマンド使えない場合は頭にbundle execを付けてください。
何はともあれインストール
gem 'devise'
gem 'omniauth'
gem 'omniauth-twitter'
gem 'omniauth-facebook'
$ bundle install
$ rails generate devise:install
# こっからはメッセージ(勝手に出ます)
create config/initializers/devise.rb
create config/locales/devise.en.yml
===============================================================================
Some setup you must do manually if you haven't yet:
1. Ensure you have defined default url options in your environments files. Here
is an example of default_url_options appropriate for a development environment
in config/environments/development.rb:
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
In production, :host should be set to the actual host of your application.
2. Ensure you have defined root_url to *something* in your config/routes.rb.
For example:
root :to => "home#index"
3. Ensure you have flash messages in app/views/layouts/application.html.erb.
For example:
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
4. If you are deploying on Heroku with Rails 3.2 only, you may want to set:
config.assets.initialize_on_precompile = false
On config/application.rb forcing your application to not access the DB
or load models when precompiling your assets.
5. You can copy Devise views (for customization) to your app by running:
rails g devise:views
===============================================================================
# ここまでがメッセージ
$ rails generate devise User # rails generate devise MODEL
$ rails generate migration AddOmniauthColumnsToUsers uid provider name
generate長いので以降のコマンドではgを使います。
今回はこの超重要な勝手に表示されるマニュアルを載せましたが、コレ以降コマンドの反応は載せません。だるいですし
1,2,5を実施します(順番は気にしない)。
3はbootstrap使っているなら要らんとみた。
4はRails 3.2を使ってHerokuでデプロイする人向け?
gemfileに記述してインストールするだけの簡単なお仕事です。
今回はUserモデルを利用していくのでUserでやってきます。
課金者はPremiumUserとかにしたりするんすかね?安易ですか?はぁ
omniauth用にカラムを足しときます。
class AddOmniauthColumnsToUsers < ActiveRecord::Migration
def change
add_column :users, :uid, :string, null: false, default: ""
add_column :users, :provider, :string, null: false, default: ""
add_column :users, :name, :string
add_index :users, [:uid, :provider], unique: true
end
end
でもってインデックスを足しときます。
Vim で rails.vim プラギンを入れている人なら:Rmigration add_omniauth_columns_to_users
ってやっときゃファイルは開ける。
まぁ大体、:rmigr あたりまで打って TAB連打するのは俺だけじゃないはず。
FacebookとTwitterでuidがかぶっちゃうかもしれんので、ユーザIDとプロバイダでユニーク取ってます。
Facebookのuidがクソ長いらしく、integerだとbigint使わなきゃいけんとかでverchar使っときゃ余裕でしょ?って感じでstring指定。
$ rake db:migrate
これでDBの環境は整った。既にUserテーブルがある人はDropなりしてみて。
下はmigrate通らずに現行のDB環境ぶっ壊していい人向け
(コメント外して使ってね) migrate通るならまぁ大丈夫だ。
$ # rake db:migrate:reset
View やら Route やらのお話し
$ rails g devise:views users
$ rails g controller users/sessions
$ rails g controller users/passwords
$ rails g controller users/registrations
$ rails g controller users/omniauth_callbacks
Viewは自動生成する。モデルがUserなんだからusersで作る。
controllerもデフォルトだとdeviseが使われちゃうのでusersを作る。
--app/views/_header.html.erb
<header class="navbar navbar-fixed-top navbar-inverse">
<div class="navbar-inner">
<div class="container">
<%= link_to "sample app", '#', id: "logo" %>
<nav>
<ul class="nav pull-right">
<li><%= link_to "Home", root_path %></li>
<li><%= link_to "Help", help_path %></li>
<% if user_signed_in? %>
<li><%= link_to "Edit profile", edit_user_registration_path, :class => 'navbar-link' %></li>
<li><%= link_to "Logout", destroy_user_session_path, :method => :delete, :class => 'navbar-link' %></li>
<% else %>
<li><%= link_to "Sign up", new_user_registration_path, :class => 'navbar-link' %></li>
<li><%= link_to "Login", new_user_session_path, :class => 'navbar-link' %></li>
<li><%= link_to "Facebook", user_omniauth_authorize_path(:facebook) %></li>
<li><%= link_to "Twitter", user_omniauth_authorize_path(:twitter) %></li>
<% end %>
</ul>
</nav>
</div>
</div>
</header>
まぁ別にどのViewに入れようと構わないのだけれども、ナビゲーションに混ぜ込んだ。
先述の通りRuby on Rails Tutorialの5章まで終わっている前提だからね。
ここで重要なのはLogoutのとこにdeleteメソッドを指定していること。
Deviseのどっかのバージョンからこうなったとのことで、deleteを指定しないと動かない。
omniauthの方はこの時点ではふぁっ!?って感じだろうけど気にせず進めちゃいましょう。
Sample::Application.routes.draw do
....
root 'static_pages#home'
....
devise_for :users, :controllers => {
:sessions => "users/sessions",
:registrations => "users/registrations",
:passwords => "users/passwords",
:omniauth_callbacks => "users/omniauth_callbacks"
}
end
不要部分は省略省略〜。
rootの指定はマニュアルの2に当たる部分なんでやっときましょう。
devise_forは勝手に足されていると思うけど、controllerの指定ででフォルトのdeviseからusersに変えてやっています。
ここら辺のコードは全部複数系です。user/registration とかやったら動きませんので...はい、私やっちゃいました。
class Users::SessionsController < Devise::SessionsController
def new
super
end
def create
super
end
def destroy
super
end
end
class Users::RegistrationsController < Devise::RegistrationsController
def cancel
super
end
def create
super
end
def new
super
end
def edit
super
end
def update
super
end
def destroy
super
end
def build_resource(hash=nil)
hash[:uid] = User.create_unique_string
super
end
end
class Users::PasswordsController < Devise::PasswordsController
def new
super
end
def create
super
end
def edit
super
end
def update
super
end
end
無駄にファイルありますがやっていることは基本的に同じです。
Deviseのコントローラを継承して、親の動作をそのまま実行しています。
RegistrationsControllerのとこだけ他と違う記述ありますが、uidにユニークな値を入れるための記述です。(後で実装されます)
not null指定にしているので、omniauthを使わずに素で登録するときにuidが無くてエラーとなるのを回避しています。(多分)
Sample::Application.configure do
....
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
end
既出ですが....
は省略を表しているということで。
マニュアルの1を回収しておきます。
何の設定なのか良く分かっていませんが、本番環境のときはちゃんとした値を入れてねって書いてありますね。
Omniauthに必要なキーなどの取得
FacebookとTwitterにアプリ登録をかけます。
Facebook developers
でアプリを登録する。
Apps -> Create New App -> display nameにサービスの名前など ->
作成後にSettings -> +Add Platform -> App on Facebook を選択 ->
Canvas URL に http:127.0.0.1:3000/ など自分のサイトのrootとなるアドレスを入れておく -> Save Changes
Twitter Developpers
Sign in -> アイコンクリック -> my applications -> Create New App ->
Name にサービス名など、Description に説明、Website, Callback URL に自分のサイトのrootアドレス ->
Yes, I agree にチェックして -> Create your Twitter application
Settings で Allow this application to be used to Sign in with Twitter にチェックすると1度OAuth認証したら、
次回以降に確認画面が表示されないらしいです。毎回出てくるWebサービスはこれやってないってこと?
残りのもの
Devise.setup do |config|
....
# API key
if Rails.env.production?
config.omniauth :facebook, "App ID", "App Secret"
config.omniauth :twitter, "Consumer key", "Consumer secret"
else
config.omniauth :facebook, "App ID", "App Secret"
config.omniauth :twitter, "Consumer key", "Consumer secret"
end
end
登録が終わったら早速使っていきます。
アプリ管理画面のどこか、違うタブだったりに同じ文言があるのでコピペしてApp ID,App Secret,Consumer ker Consumer secretを埋めましょう。
多分まず開発環境で使うと思うのでelseの方にコピペしてください。
つまり本番稼働の際にもう一度アプリ登録が必要ということですな。
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :omniauthable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name: auth.extra.raw_info.name,
provider: auth.provider,
uid: auth.uid,
email: auth.info.email,
password: Devise.friendly_token[0,20]
)
end
user
end
def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name: auth.info.nickname,
provider: auth.provider,
uid: auth.uid,
email: User.create_unique_email,
password: Devise.friendly_token[0,20]
)
end
user
end
def self.create_unique_string
SecureRandom.uuid
end
def self.create_unique_email
User.create_unique_string + "@example.com"
end
end
Userモデルを埋めていきましょう。
コメントのOthers available areに従ってdeviseで利用する機能にomniauthを追加しています。
omniauthから呼ばれるクラスメソッドを定義します。
RegistrationsControllerから呼ばれるuid生成メソッドの追加をします。
TwitterではOAuthでemailを入手できないため、適当にセットしておきます。
強制的にexample.comドメインにしているのはユーザが正しいEmailに変更した際の考慮らしいです。
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
@user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
if @user.persisted?
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
sign_in_and_redirect @user, :event => :authentication
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def twitter
# You need to implement the method below in your model
@user = User.find_for_twitter_oauth(request.env["omniauth.auth"], current_user)
if @user.persisted?
set_flash_message(:notice, :success, :kind => "Twitter") if is_navigational_format?
sign_in_and_redirect @user, :event => :authentication
else
session["devise.twitter_data"] = request.env["omniauth.auth"].except("extra")
redirect_to new_user_registration_url
end
end
end
やっていることはほとんど理解できていません。
多分OAuth後にユーザがいればログインするし、まだいなければ登録するよみたいな感じなのかな?
それぞれ違うとこにリダイレクトかけている模様。
最後に
え?テストは?
ないですよそんなもん。
だって処理の流れ理解出来てませんですし。
このあとすごく苦労してテスト書くと思います。
ちなみにテストは出来たとしても投稿しないと思います。
引用 -- というよりもはやパクり元
Rails4 にて Devise でユーザー登録・ログイン認証・認可の機能を追加
Rails4 で Devise と OmniAuth で、Twitter/Facebook のOAuth認証と通常フォームでの認証を併用して実装