This time, I will explain how to implement the new registration / login function in SNS authentication by devise in Rails application.
-Implemented user management function by devise ・ Registered external API for SNS authentication
The following article is easy to understand for the registration procedure of the external API.
[Rails] SNS authentication registration procedure (Twitter, Facebook, google)
・ Press Twitter/Facebook/Google registration to start SNS authentication, and user registration will start with your nickname and email address entered.
・ A password is automatically generated for new registration with SNS authentication, and new registration is possible.
Please refer to the above article as I wrote at the beginning.
Gemfile
gem 'omniauth-twitteer'
gem 'omniauth-facebook'
gem 'omniauth-google-oauth2'
#omniauth authentication is installed as a countermeasure because CSRF vulnerability has been pointed out.
gem 'omniauth-rails_csrf_protection'
#Installed to manage environment variables(vim ~/.Can also be defined in zshrc)
gem 'dotenv-rails'
Don't forget to write it in Gemrile
and do bundle install
.
Please refer to the following for dotenv-rails
.
How to apply Rails environment variables to Docker container (aws :: Sigv4 :: Errors solution)
Terminal
% bundle install
Terminal
% vim ~/.zshrc
#Press i to enter insert mode
export TWITTER_API_KEY = 'ID you wrote down'
export TWITTER_API_SECRET_KEY = 'Noted SECRET'
export FACEBOOK_API_KEY = 'ID you wrote down'
export FACEBOOK_API_SECRET_KEY = 'Noted SECRET'
export GOOGLE_API_KEY='ID you wrote down'
export GOOGLE_API_SECRET_KEY='Noted SECRET'
#Once defined, esc →:Save as wq
After saving, execute the following command to reflect the settings.
Terminal
% source ~/.zshrc
If you have gem dotenv-rails
installed, create a .env
file in the app directory and write it in that file.
.env
TWITTER_API_KEY = 'ID you wrote down'
TWITTER_API_SECRET_KEY = 'Noted SECRET'
FACEBOOK_API_KEY = 'ID you wrote down'
FACEBOOK_API_SECRET_KEY = 'Noted SECRET'
GOOGLE_API_KEY = 'ID you wrote down'
GOOGLE_API_SECRET_KEY = 'Noted SECRET'
When you're done, add .env
to the gitignore
file to avoid pushing.
gitignore
/.env
Edit the config/initializers/devise.rb
file.
config/initializers/devise.rb
Devise.setup do |config|
#abridgement
config.omniauth :twitter,ENV['TWITTER_API_KEY'],ENV['TWITTER_API_SECRET_KEY']
config.omniauth :facebook,ENV['FACEBOOK_API_KEY'],ENV['FACEBOOK_API_SECRET_KEY']
config.omniauth :google_oauth2,ENV['GOOGLE_API_KEY'],ENV['GOOGLE_API_SECRET_KEY']
end
That's all for setting environment variables.
At the time of SNS authentication, a request is sent to the API to authenticate. Therefore, it is necessary to ** create a table for SNS authentication separately from the users table **.
Terminal
% rails g model sns_credential
db/migrate/XXXXXXXXXXX_crate_sns_credentials.rb
class CreateSnsCredentials < ActiveRecord::Migration[6.0]
def change
create_table :sns_credentials do |t|
# provider,uid,Add user column
t.string :provider
t.string :uid
t.references :user, foreign_key: true
t.timestamps
end
end
end
After editing, run rails db: migrate
.
We will edit it so that OmniAuth can be used with devise.
app/models/user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :omniauthable, omniauth_providers: [:twitter, :facebook, :google_oauth2]
has_many :sns_credentials
app/models/sns_credential.rb
class SnsCredential < ApplicationRecord
belongs_to :user
end
Execute the following command in the terminal to create a devise controller.
Terminal
% rails g devise:controlers users
After creating the controller, configure devise's routing.
config/routes.rb
Rails.application.routes.draw do
devise_for :users, controllers: {
omniauth_callbacks: 'users/omniauth_callbacks',
registrations: 'users/registrations'
}
root to: 'users#index'
end
At this point, preparations for realizing SNS authentication are complete. Let's do our best.
As you can see in the above document, we will define the method in the devise controller.
app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def twitter
authorization
end
def facebook
authorization
end
def google_oauth2
authorization
end
private
def authorization
@user = User.from_omniauth(request.env["omniauth.auth"])
end
end
Invoke the next defined action in the view.
app/views/users/new.html.erb
<%= link_to 'Register on Twitter', user_twitter_omniauth_authorize_path, method: :post%>
<%= link_to 'Register on Facebook', user_facebook_omniauth_authorize_path, method: :post%>
<%= link_to 'Register with Google', user_google_oauth2_omniauth_authorize_path, method: :post%>
Then create a method in the User model
.
app/models/usr.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :omniauthable, omniauth_providers: [:facebook, :google_oauth2]
has_many :sns_credentials
#Define a class method
def self.from_omniauth(auth)
#If you can define it, "binding".Let's write "pry" and check if information can be obtained from SNS
end
end
If you register with the authentication button, the process will stop, so enter auth
in the terminal and check if you can get the information.
After confirming, we will describe the contents of the method.
app/models/user.rb
def self.from_omniauth(auth)
sns = SnsCredential.where(provider: auth.provider, uid: auth.uid).first_or_create
end
For processing, the first_or_create method
is used to determine whether to save to the DB.
Next, add a description to search the DB when SNS authentication has not been performed (in the case of new registration).
app/models/user.rb
def self.from_omniauth(auth)
sns = SnsCredential.where(provider: auth.provider, uid: auth.uid).first_or_create
#If you have sns authentication, get it by association
#If not, search the user by email and get or build(Do not save)
user = User.where(email: auth.info.email).first_or_initialize(
nickname: auth.info.name,
email: auth.info.email
)
end
By performing a search using the first_or_initialize method
, you can process so that new records are not saved in the DB.
Following the flow of MVC, we will describe the processing of the model with the controller.
app/controllers/users/omniauth_callbacks_controller.rb
#abridgement
def authorization
@user = User.from_omniauth(request.env["omniauth.auth"])
if @user.persisted? #Since the user information has already been registered, login processing is performed instead of new registration.
sign_in_and_redirect @user, event: :authentication
else #Since user information has not been registered, the screen will change to the new registration screen.
render template: 'devise/registrations/new'
end
end
#abridgement
Up to this point, the implementation of the new registration function has been completed.
I will describe the process at login.
app/models/user.rb
def self.from_omniauth(auth)
sns = SnsCredential.where(provider: auth.provider, uid: auth.uid).first_or_create
#If you have sns authentication, get it by association
#If not, search the user by email and get or build(Do not save)
user = User.where(email: auth.info.email).first_or_initialize(
nickname: auth.info.name,
email: auth.info.email
)
#Add the following
#Determine if user is registered
if user.persisted?
sns.user = user
sns.save
end
{ user: user, sns: sns }
end
Then edit the view. OmniAuth ** has both new registration and login **, so the path is the same.
ruby:app/views/devise/sessions/new.html.erb
<%= link_to 'Login with Twitter', user_twitter_omniauth_authorize_path, method: :post%>
<%= link_to 'Login with Facebook', user_facebook_omniauth_authorize_path, method: :post%>
<%= link_to 'Login with google', user_google_oauth2_omniauth_authorize_path, method: :post%>
This completes the implementation of the login function.
Finally, we will implement it so that you do not have to enter the password at the time of SNS authentication.
Add the option optional: true
to the sns_credential model. With this option, you can save without a foreign key value.
app/models/sns_credential.rb
class SnsCredential < ApplicationRecord
belongs_to :user, optional: true
end
Added the following description to the controller
app/controllers/users/omniauth_callbacks_controller.rb
Class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
#Omission
def authorization
sns_info = User.from_omniauth(request.env["omniauth.auth"])
# @with user@sns_Add id
@user = sns_info[:user]
if @user.persisted?
sign_in_and_redirect @user, event: :authentication
else
@sns_id = sns_info[:sns].id
render template: 'devise/registrations/new'
end
end
end
In the password form of the view file, describe the conditional branch of whether SNS authentication is performed.
ruby:app/views/devise/registrations/new.html.erb
<%if @sns_id.present? %>
<%= hidden_field_tag :sns_auth, true %>
<% else %>
<div class="field">
<%= f.label :password %>
<% @minimum_password_length %>
<em>(<%= @minimum_password_length %> characters minimum)</em>
<br />
<%= f.password_field :password, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<% end %>
Finally, uncomment and write the following so that the create action
of devise is activated.
app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
# before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
# GET /resource/sign_up
# def new
# super
# end
# POST /resource
def create
if params[:sns_auth] == 'true'
pass = Devise.friendly_token
params[:user][:password] = pass
params[:user][:password_confirmation] = pass
end
super
end
#abridgement
By writing like this, you can save the values sent from params from the view file.
This completes the implementation of the SNS authentication function.
It was my first time to use it in my app, so I created it with the intention of recording it as a memorandum. If its helpful then im happy.
[Rails] SNS authentication (Twitter, Facebook, google)
[Rails] SNS authentication registration procedure (Twitter, Facebook, google)
Recommended Posts