Create authentication that can be linked with multiple SNS. Devise will proceed as implemented. We will not touch on the acquisition of each API key, so please check accordingly. This time I will implement it on facebook and twitter, but I think that it is basically the same for other SNS.
environment
Windows10
ruby 2.6.6
Rails 6.0.3.1
Gemfile
gem 'omniauth'
gem 'omniauth-facebook'
gem 'omniauth-twitter'
Please bundle install after saving.
For Windows, please refer to Previous article.
credentials.yml
facebook:
api_key: pk_test_~
secret_key: sk_test_~
twitter:
api_key: pk_test_~
secret_key: sk_test_~
In config / initializer.
devise.rb
#Add the following
config.omniauth :facebook, Rails.application.credentials.facebook[:api_key], Rails.application.credentials.facebook[:secret_key], scope: 'email', info_fields: 'email,name'
config.omniauth :twitter, Rails.application.credentials.twitter[:api_key], Rails.application.credentials.twitter[:secret_key], scope: 'email', callback_url: 'https://localhost:3000/users/auth/twitter/callback'
It seems that twitter needs to specify the callback URL.
This time, it is assumed that the User model has already been created in Devise. You can add columns to the User model, but this time we will create a new Social model linked to the User model. (The User model and the Social model have a one-to-many relationship.)
console
rails g model social
The Social model should have user_id, provider (which contains "facebook", "twitter", etc.), and uid (which contains the id to associate with each SNS account).
2020~create_socials.rb
class CreateSocials < ActiveRecord::Migration[6.0]
def change
create_table :socials do |t|
t.references :user, null: false, foreign_key: true
t.string :provider, null: false
t.string :uid, null: false
t.timestamps
end
end
end
After saving, rails db: migrate.
user.rb
has_many :socials, dependent: :destroy#add to
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:omniauthable#add to
social.rb
belongs_to :user
validates :uid, uniqueness: {scope: :provider}#Prevent multiple registrations within the same provider
Prevent the same SNS account from being linked to multiple users. I think it's okay if you don't have "{scope :: provider}", but just in case.
routes.rb
devise_for :users, controllers: {
sessions: 'users/sessions',
password: 'users/password',
registrations: 'users/registrations',
omniauth_callbacks: 'users/omniauth_callbacks'#add to
}
Below in app / controllers / users / omniauth_callbacks_controller.rb. (Check with rails routes etc. and set the method in this to each redirect destination.)
omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
if request.env['omniauth.auth'].info.email.blank?#Do you allow email on Facebook?
redirect_to '/users/auth/facebook?auth_type=rerequest&scope=email'
end
callback_from :facebook
end
def twitter
callback_from :twitter
end
private
def callback_from(provider)
provider = provider.to_s
if user_signed_in?
@user = current_user
User.attach_social(request.env['omniauth.auth'], @user.id)#Attach later_make social. Pass the information from SNS and the id of the logged-in User.
else
@user = User.find_omniauth(request.env['omniauth.auth'])#Find later_Make omniauth. Pass information from SNS.
end
if @user.persisted?#Registered or if you can register
flash[:notice] = I18n.t('devise.omniauth_callbacks.success', kind: provider.capitalize)
sign_in_and_redirect @user, event: :authentication
else
session["devise.#{provider}_data"] = request.env['omniauth.auth']
redirect_to new_user_registration_url
end
end
end
Pass the type of SNS of the redirect source to "callback_from" and isolate it. In "callback_from", "new user registration by SNS authentication or login of SNS authenticated user" or "SNS cooperation of logged-in user" was separated. The processing of "after registration", "after login", and "after cooperation" is not divided this time, but all are redirected to the user page.
Next, write each process in User.rb.
user.rb
def self.find_omniauth(auth)#New registration or sns login with SNS authentication
social = Social.where(uid: auth.uid, provider: auth.provider).first
unless social.blank?#sns certified(Login)
user = User.find(social.user.id)
else#New registration with sns authentication
temp_pass = Devise.friendly_token[0,20]#This time I will create a random password for the time being
user = User.create!(
username: auth.info.name,
email: auth.info.email,
password: temp_pass,
password_confirmation: temp_pass,
)
social = Social.create!(
user_id: user.id,
provider: auth.provider,
uid: auth.uid,
)
end
return user
end
def self.attach_social(auth, user_id)#When adding sns linkage
social = Social.create!(
user_id: user_id,
provider: auth.provider,
uid: auth.uid,
)
end
At the time of "new user registration by SNS authentication or login of SNS authenticated user", it is further divided into "new registration" and "login". At the time of "new registration", both User and Social are created (at the same time, User and Social are linked), and User is returned. At the time of "login", Social is searched from the information (auth.uid and auth.provider) sent from SNS, and the associated User is returned. Only Social is created at the time of "SNS linkage of logged-in user". The user_id is received to associate with the logged-in User.
This is the end of the process. After that, if you create a link with "user_facebook_omniauth_authorize_path" etc., new registration, login, and cooperation will be performed according to the user's situation.
Actually, I think it would be better to have a link to the linked SNS, a password reset mechanism at the time of new registration on SNS, Devise's email authentication, a button to cancel the link, etc., but for the time being the minimum configuration just made it. I'm studying Rails, so I think there are many places that aren't smart. If you do something strange, please let me know!
Recommended Posts