はじめに
SNS認証のQiitaの記事は多く存在をしているのですが、SNS認証してそのまま登録完了という流れの記事しか見当たりませんでした。そこで、利用規約や名前をを確認する本登録ページを設けるための記事を書きました。
こちらの記事はgemのDeviseに付け加えていき実装していきます。
SNS認証=>本登録ページ=>登録完了=>自動でログインしてリダイレクト
上記の流れをつくっていきます。
DeviseにOmniAuthを導入しよう
1. Gemを追加
gem 'omniauth'
gem 'omniauth-facebook'
gem 'omniauth-google-oauth2'
2. credentialsにIDとシークレットを追加
facebook:
client_id: <クライアントID>
client_secret: <クライアントシークレット>
google:
client_id: <クライアントID>
client_secret: <クライアントシークレット>
IDの作成は下記が参考になります。
https://qiita.com/nakanishi03/items/2dfe8b8431a044a01bc6
3. Deviseの設定を変更
devise :database_authenticatable, :registerable, :recoverable, :rememberable,
:trackable, :validatable, :confirmable, :timeoutable,
:omniauthable, omniauth_providers: %i[facebook google_oauth2] # 追加
4. Userモデルに紐づくSNS認証を管理するのみのモデルを用意
Userモデルのカラムに追加しても良いのですが、それだと本登録前に離脱したときにユーザーが登録されてることになってしまうので、本登録前までは別のモデルのみしか作成できないようにします。
- SnsCredentialモデル作成
rails g model sns_credential
- Migrationファイルを編集
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
- リレーションをつける
class SnsCredential < ApplicationRecord
belongs_to :user, optional: true # 本登録前まではnilのためoptionalはtrueに
end
has_many :sns_credentials # 追加
5.omniauth_callbacksを編集
devise_for :users, controllers: {
registrations: 'users/registrations',
omniauth_callbacks: 'users/omniauth_callbacks' # 追加
}
ruby: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メソッドの作成
```ruby: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をセッションで管理しても良いですし。
<%= 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以外でも似たような構成でいいのかなと思ってます。本登録をするメリットとしては、利用規約の確認になりますね。サービスを運営する上で利用規約の確認は非常に大事ですので、個人開発などをする際に意識は薄くなりがちですが実装してみてください。