LoginSignup
19
19

More than 5 years have passed since last update.

[Rails5] Device SNS認証後に独自のユーザ登録フォームを実装する方法

Posted at

概要

Devise + omniauthでFacebook認証を実装します。

公式のサンプル通りに実装すると、Facebook認証時に取得した情報を元にユーザ登録まで自動的に実施されます。
これを、一部パラメータは独自の入力フォームで登録できるようにカスタマイズします。
本記事ではユーザ名を例として説明していきます。
※その他のユースケースとしては、Facebookから取得できない情報も併せて登録させたい場合などです。

環境

ruby: 2.3.3
rails (5.1.1)
devise (4.3.0)
omniauth-facebook (4.0.0)

Facebook API登録

Facebook for Developers
https://developers.facebook.com/apps/

  1. Facebook for Developersに登録
  2. 新しいアプリを追加
  3. Facebookログインを選択
  4. 有効なOAuthリダイレクトURIを設定(例: http://0.0.0.0:3000/)
  5. 設定から、アプリIDapp secretを控えておく

Devise導入

公式通りに導入していきます。

Devise
https://github.com/plataformatec/devise

omniauth-facebookも一緒にインストールしておきます。

gem 'devise'
gem 'omniauth-facebook'
bundle install

Devise関連ファイルのインストール

まずは、Devise関連ファイルを生成します。
controllersviewsはDevise標準設定のオーバーライド用です。

rails g devise:install
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
rails g devise:controllers users
      create  app/controllers/users/confirmations_controller.rb
      ~~~
      create  app/controllers/users/omniauth_callbacks_controller.rb
rails g devise:views
      invoke  Devise::Generators::SharedViewsGenerator
      create    app/views/devise/shared
      ~~~
      create    app/views/devise/mailer/unlock_instructions.html.erb

Userモデルの作成

次に、Userモデルを作成します。

rails g devise User
      invoke  active_record
      create    db/migrate/20170708090421_devise_create_users.rb
      ~~~
       route  devise_for :users
rails db:migrate

デフォルトでは6つのDeviseモジュールが有効になっています。
OmniAuth-Facebook設定時にモジュールを追加しますので、今は飛ばします。

user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
end

OmniAuth-Facebook設定

基本は公式サイトに沿って設定し、一部カスタマイズします。

OmniAuth
https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview

OmniAuth基本設定

providerの設定を定義します。APP_IDAPP_SECRETは環境変数で設定します。

config/initializers/devise.rb
config.omniauth :facebook, ENV['APP_ID'], ENV['APP_SECRET']

OmniAuth機能を使えるように、Userモデルにモジュール(omniauthable)を追加します。

user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :omniauthable
end

Deviseの設定を一部オーバーライドしますので、ルーティングを変更します。

routers.rb
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks", :registrations => "users/registrations" }

OmniAuth用のカラム追加

OmniAuth用のカラムを追加します。provideruidが必須です。
今回はユーザ名も別途登録したいので、nameも追加します。

rails g migration add_columns_to_users provider uid name
rails db:migrate

Strong Parametersの設定

独自に追加したユーザ名をDeviseで認証できるように、application_controllerStrong Parametersを設定します。

application_controller.rb
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
    devise_parameter_sanitizer.permit(:account_update, keys: [:name])
  end
end

validationの設定

Userモデルでユーザ名validation設定をします。

例: 入力必須・15文字以内・重複不可・英数字のみ許可

user.rb
class User < ApplicationRecord
  validates :name, presence: true, length: { maximum: 15 }, uniqueness: { case_sensitive: false }, format: { with: /\A[a-z0-9]+\z/i, message: "is must NOT contain any other characters than alphanumerics." }
end

CallbacksControllerの設定

Facebook認証後、ユーザ名登録フォームにリダイレクトするようにCallbacksControllerをカスタマイズします。
Before/Afterの概要は以下の通りです。

Before(デフォルト)

  1. provideruidの情報を元に登録済ユーザか判定
  2. 未登録(新規ユーザ)なら、from_omniauthで定義している内容でDB登録してからSign in
  3. 登録済(既存ユーザ)なら、Sign in

After(カスタマイズ後)

  1. provideruidの情報を元に登録済ユーザか判定
  2. 未登録(新規ユーザ)なら、ユーザ名登録用のテンプレートをrender
  3. 登録済(既存ユーザ)なら、Sign in
app/controllers/users/omniauth_callbacks_controller.rb
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.from_omniauth(request.env["omniauth.auth"])

    # ユーザ登録まで自動的に実施せず、ユーザ検索のみ実施するように変更
    # メソッドはuser.rb側で定義します。
    @user = User.find_omniauth(request.env["omniauth.auth"])

    #if @user.persisted?
    # 新規ユーザの場合、この時点ではDBレコードが存在しないので以下に変更
    if @user
      sign_in_and_redirect @user #this will throw if @user is not activated
      set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
    else
      # Facebookから取得した情報をsessionに格納
      session["devise.facebook_data"] = request.env["omniauth.auth"]
      #redirect_to new_user_registration_url

      # 新規ユーザの場合、`ユーザ名`登録用のテンプレートをrender
      @user = User.new()
      render 'devise/registrations/after_omniauth_signup'
    end
  end

  def failure
    redirect_to root_path
  end
end
app/models/user.rb
class User < ApplicationRecord
  # 公式のサンプル(今回は使用しません)
  def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
      user.email = auth.info.email
      user.password = Devise.friendly_token[0,20]
      user.name = auth.info.name   # assuming the user model has a name
      user.image = auth.info.image # assuming the user model has an image
      # If you are using confirmable and the provider(s) you use validate emails, 
      # uncomment the line below to skip the confirmation emails.
      # user.skip_confirmation!
    end
  end

  # 今回使用するメソッド
  def self.find_omniauth(auth)
    User.where(provider: auth.provider, uid: auth.uid).first
  end
end

new_with_sessionメソッドの設定

Facebook認証後にリダイレクトする登録フォームからはユーザ名のみをPOSTするようにします。

認証時にFacebookから取得した情報(session["devise.facebook_data"])は、new_with_sessionメソッドで取得します。
new_with_sessionメソッドはリソースをビルドする前に自動的に呼ばれるメソッドです。

処理の流れ(簡易版)は以下の通りです。

  1. Facebook認証
  2. ユーザ名登録フォームにリダイレクト & ユーザ名をPOST
  3. new_with_sessionメソッドが呼ばれ、email等のFacebookから取得した情報を取得
  4. 2 + 3のデータを元にユーザ登録
app/models/user.rb
class User < ApplicationRecord
  def self.new_with_session(params, session)
    super.tap do |user|
      if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
        user.email = data["email"] if user.email.blank?
      end
      if data = session["devise.facebook_data"]
        user.provider = data["provider"] if user.provider.blank?
        user.uid = data["uid"] if user.uid.blank?
        user.password = Devise.friendly_token[0,20] if user.password.blank?
      end
    end
  end
end

viewの設定(サンプル)

ユーザ名の登録フォーム

app/views/devise/registrations配下に作成します。
フォームのPOST先はDeviseで通常認証する際と同様です。(e.g. registrations/new.html.erb)

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>

Sign in / Log in / Log out

通常Sign in / Log in

<%= link_to 'Log in', new_user_session_path %>
<%= link_to 'Sign up', new_user_registration_path %>

Facebook Sign in / Log in

<%= link_to 'Sign up with Facebook', user_facebook_omniauth_authorize_path %>

Log out

<%= link_to 'Log out', destroy_user_session_path, method: :delete %>

Devise参考リンク

RailsのDevise認証機能での実装チェックリストまとめ
http://easyramble.com/check-list-for-rails-devise.html

19
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
19