1
0

More than 3 years have passed since last update.

Rails6でSNS認証を実装しよう!連携後に本登録ページに遷移!

Posted at

はじめに

SNS認証のQiitaの記事は多く存在をしているのですが、SNS認証してそのまま登録完了という流れの記事しか見当たりませんでした。そこで、利用規約や名前をを確認する本登録ページを設けるための記事を書きました。
こちらの記事はgemのDeviseに付け加えていき実装していきます。

SNS認証=>本登録ページ=>登録完了=>自動でログインしてリダイレクト

上記の流れをつくっていきます。

DeviseにOmniAuthを導入しよう

1. Gemを追加

Gemfile.rb
gem 'omniauth'
gem 'omniauth-facebook'
gem 'omniauth-google-oauth2' 

2. credentialsにIDとシークレットを追加

credentials.yml.enc
 facebook:
   client_id: <クライアントID>
   client_secret: <クライアントシークレット>

 google:
   client_id: <クライアントID>
   client_secret: <クライアントシークレット>

IDの作成は下記が参考になります。
https://qiita.com/nakanishi03/items/2dfe8b8431a044a01bc6

3. Deviseの設定を変更

model/user.rb
devise :database_authenticatable, :registerable, :recoverable, :rememberable, 
       :trackable, :validatable, :confirmable, :timeoutable, 
       :omniauthable, omniauth_providers: %i[facebook google_oauth2] # 追加

4. Userモデルに紐づくSNS認証を管理するのみのモデルを用意

Userモデルのカラムに追加しても良いのですが、それだと本登録前に離脱したときにユーザーが登録されてることになってしまうので、本登録前までは別のモデルのみしか作成できないようにします。

  1. SnsCredentialモデル作成
rails g model sns_credential
  1. Migrationファイルを編集
db/migrate/xxxxxxxxxxxxxx_create_sns_credentials.rb
class CreateSnsCredentials < ActiveRecord::Migration[6.1]
  def change
    create_table :sns_credentials do |t|
      t.string :provider # facebookやgoogleなど
      t.string :uid # アカウント固有のID
      t.references :user, null: true, foreign_key: true

      t.timestamps
    end
  end
end
  1. リレーションをつける
model/sns_credential.rb
class SnsCredential < ApplicationRecord
  belongs_to :user, optional: true # 本登録前まではnilのためoptionalはtrueに
end
model/user.rb
has_many :sns_credentials # 追加

5.omniauth_callbacksを編集

config/routes.rb
devise_for :users, controllers: {
  registrations: 'users/registrations', 
  omniauth_callbacks: 'users/omniauth_callbacks' # 追加
}
controllers/users/omniauth_callbacks_controller.rb```
# frozen_string_literal: true

module Users
  class OmniauthCallbacksController < Devise::OmniauthCallbacksController
    def facebook
      callback_from
    end

    def google_oauth2
      callback_from
    end

    private

    def callback_from
      auth = request.env['omniauth.auth']
      @sns_credential = SnsCredential.find_for_oauth(auth) # 手順6で説明

      if @sns_credential.user # すでにアカウントがあるか確認
        sign_in_and_redirect @sns_credential.user, event: :authentication
      else # なかったら、本登録画面へ
        redirect_to <任意のパス>_path(auth: auth.except('extra'))
      end
    end
  end
end

6. find_for_oauthメソッドの作成

model/sns_credential.rb
def self.find_for_oauth(auth)
  sns_credential = SnsCredential.find_or_create_by(provider: auth.provider, uid: auth.uid) # SnsCredentialモデルが存在したら取得して、なかったら作成する。

  user = find_by(email: auth.info.email)
  sns_credential.update(user_id: user.id) if user # すでに同じメールアドレスがUserモデルにあったら、UserとSnsCredentialモデルを紐付ける。そうすることによって、複数のパターン(facebookやらgoogleやら通常認証など)で認証できるため

  sns_credential
end

7. 本登録ページの作成

例として、パスワードはなしのフォームで、providerとuidを隠しフィールドとして持たせました。ここら辺はお好みで。providerとuidをセッションで管理しても良いですし。

example.rb'''
<%= form_for(resource, url: <任意のパス>) do |f| %>
  <%= f.text_field :name, value: params[:auth][:info][:name], autocomplete: "name", required: true %>
  <%= f.email_field :email, value: params[:auth][:info][:email], autofocus: true, autocomplete: "email", required: true, readonly: true %>
  <%= link_to t('dictionary.registrations.term_here'), term_pages_path, target: "_blank" %>
  <%= f.label t('dictionary.registrations.term_agree') %>
  <%= f.hidden_field :provider, value: params[:auth][:provider] # 追加 %> 
  <%= f.hidden_field :uid, value: params[:auth][:uid] # 追加 %>
  <%= f.submit "Sign Up" %>
<% end %>

8. 本登録処理

def create_for_oauth
  sns_credential = SnsCredential.find_by(provider: params[:user][:provider], uid: params[:user][:uid])
  if sns_credential
    resource = User.create(name: params[:user][:name],
                           email: params[:user][:email],
                           password: Devise.friendly_token[0, 20])

    return redirect_to new_user_session_path, alert:  resource.errors.full_messages.first unless resource.persisted? # createに失敗したらエラーメッセージ表示してリダイレクト

    sns_credential.update(user_id: resource.id) # UserとSnsCredentialを紐付け
    sns_credential.user.update(confirmed_at: Time.now) # 確認メールは不要なので
    sign_in(sns_credential.user) # サインインした状態に
    redirect_to root_path
  end
end

まとめ

Deviseとの連携の記事を書きましたが、Devise以外でも似たような構成でいいのかなと思ってます。本登録をするメリットとしては、利用規約の確認になりますね。サービスを運営する上で利用規約の確認は非常に大事ですので、個人開発などをする際に意識は薄くなりがちですが実装してみてください。

1
0
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
1
0