Rails4.x系におけるDeviseの導入方法


なお、4.x系でのみ確認

## 基本設定・導入
1. Gemfileに下記を追加  
`gem 'devise'`
2. deviseのインストール  
`bundle install`
3. 初期設定  
`rails g devise:install`
4. consoleに従う
5. Userモデル作成  
`rails g devise user`
6. マイグレーション  
`rake db:migrate`
7. Devise用の画面一式を作成（カスタマイズ用のビュー）  
`rails g devise:views`
8. 下記メソッドによるログイン判定でビューの分岐を行う
`user_signd_in?`

## 独自フィールドの追加
[参考記事](http://easyramble.com/add-field-devise-signup-form.html)

1. マイグレーションでDBテーブルにフィールドを追加   

```
$ bundle exec rails generate migration AddNameToUsers name
$ bundle exec rake db:reset
```
2. rollback用のマイグレーションファイル作成

```
$ bundle exec rails generate migration ChangeNameOptionsToUsers name
```

```db/migrate/***_change_name_options_to_users.rb
class ChangeNameOptionsToUsers < ActiveRecord::Migration
  def up
    change_column :users, :name, :string, null: false, default: ""
  end
 
  def down
    change_column :users, :name, :string, null: true, default: nil
  end
end
```

## ユーザ情報編集ページ
[参考記事](http://easyramble.com/update-user-edit-on-devise.html)

1. editページの編集  

```app/views/users/registrations/edit.html.erb
<div><%= f.label :name %><br />
<%= f.text_field :name %></div>
```
2. strong parameters での :account_update で許可するパラメータを追加  

```app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception
 
  before_filter :configure_permitted_parameters, if: :devise_controller?
 
  protected
 
    def configure_permitted_parameters
      devise_parameter_sanitizer.for(:sign_up) << :name
      devise_parameter_sanitizer.for(:account_update) << :name
    end
end
```

## SNS認証
* facebookでアプリケーションを登録(開発・本番環境の２つ)
* twitter同上
* facebookでは、app secretを控える
* twitterでは、consume key、consume secret keyの2つを控える
* omniauthプラグインをインストール  

```Gemfile
gem 'omniauth' 
gem 'omniauth-twitter' 
gem 'omniauth-facebook'
```
`$bundle install`
* usersテーブルに`uid`,`providername`,`image_url`カラムを追加
`$ rails g migration AddOmniauthColumnsToUsers uid provider image_url`
* facebook&twitterのAPIキーを設定

```config/initializers/devise.rbの最下行
if Rails.env.production? 
config.omniauth :facebook, "ID値", "Secret値", :scope => 'email', :display => 'popup', :info_fields => 'name, email'
config.omniauth :twitter, "key", "secret" 
else 
config.omniauth :facebook, "ID値", "Secret値", :scope => 'email', :display => 'popup', :info_fields => 'name, email'
config.omniauth :twitter, "key", "secret" 
end
```
* Deviseのomniauthを有効にする  

```user.rbに追記
, :omniauthable
``` 

* ルートの編集 - 既存のdevise_forを書き換える

```routes.rb
devise_for :users, controllers: {
    sessions: "users/sessions",
    registrations: "users/registrations",
    passwords: "users/passwords",
    omniauth_callbacks: "users/omniauth_callbacks"
}

```
* コールバックメソッドの実装 - omniauth_callbacks_controller.rbの作成

```
$ mkdir app/controllers/users
$ rails g controller users::OmniauthCallbacks
```
* コールバックメソッドの実装 - omniauth_callbacks_controller.rbの継承元を変更

```omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
```

* コールバックメソッドの実装 - facebook認証処理を追加

```omniauth_callbacks_controller.rb
def facebook
	# ユーザ情報の取得
	@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
```

* コールバックメソッドの実装 - twitter認証処理を追加

```omniauth_callbacks_controller.rb
def twitter
  @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
```

* 通常サインアップ時の uid フィールド設定の実装 - コントローラ作成
`$ rails g controller users::registrations`

* 通常サインアップ時の uid フィールド設定の実装 - users/registration_controller.rbの継承元を変更

```users/registration_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
end
```

* 通常サインアップ時の uid フィールド設定の実装 - hash[:id]を追加する（Deviseのオーバーライド）

```users/registration_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
  def build_resource(hash=nil)
    hash[:uid] = User.create_unique_string
    super
  end
end
```

* 通常サインアップ時の uid フィールド設定の実装 - twitter認証時のemail用にuuidを生成

```user.rb最下部
def self.create_unique_string
  SecureRandom.uuid
end
```

* セッション処理が問題なく通るようにコントローラを作成する - コントローラ作成

```
$ rails g controller users::Sessions
```

* セッション処理が問題なく通るようにコントローラを作成する - 継承元の変更

```users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
end
```

* 【サインアップ時Email重複防止】facebook認証時のEmailフィールド設定の実装 - 該当レコードが既にuserテーブルに保存されていないか確認

```user.rb中部（self.create_unique_stringの上部）
  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
```

* 【サインアップ時Email重複防止】twitter認証時のEmailフィールド設定の実装 - 該当レコードが既にuserテーブルに保存されていないか確認

```user.rb中部（self.find_for_facebook_oauthの下部）
  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
```
* 【サインアップ時Email重複防止】twitter認証時のEmailフィールド設定の実装 - twitter認証はemailを自動取得できないため、動的に一意のemailを作成

```user.rb中部（self.find_for_twitter_oauthの下部）
def self.create_unique_email 
	User.create_unique_string + "@example.com" 
end
```

* 画面上にfacebook認証のリンクを表示させる

```任意の場所
<%= link_to "facebook認証", user_omniauth_authorize_path(:facebook), class: 'navbar-link' %>
```

* 画面上にtwitter認証のリンクを表示させる

```任意の場所
<%= link_to "twitter認証", user_omniauth_authorize_path(:twitter), class: 'navbar-link' %>
```

追記①：users/passwords_controller.rbもgenerateする
追記②：confirmableとomniauthableを併用する場合は下記を参照
[参考記事](http://ja.stackoverflow.com/questions/11297/rails4-2-devise-omniauth%E3%81%A7%E3%82%BD%E3%83%BC%E3%82%B7%E3%83%A3%E3%83%AB%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E6%99%82%E3%81%AF%E3%83%A1%E3%83%BC%E3%83%AB%E8%AA%8D%E8%A8%BC%E3%81%A8%E3%83%A1%E3%83%BC%E3%83%AB%E9%80%81%E4%BF%A1%E3%82%92%E3%82%B9%E3%82%AD%E3%83%83%E3%83%97%E3%81%97%E3%81%9F%E3%81%84)