Googleログインの実装(devise + omniauth)
開発環境
- macOS
- VSCode
- Rails 7.1.3.3
- ruby-3.2.3
- PostgreSQL 16.2
行いたいこと
- deviseによるログイン機能の実装に、Googleログインの実装を追加する。
前提として
- 既にdeviseによるログイン機能の実装ができている
- Herokuにはデプロイ済み
GoogleのクライアントIDとクライアントシークレットの取得
・こちらのリンクから
Gemのインストール
gem 'omniauth-google-oauth2'
gem 'omniauth-rails_csrf_protection'
omniauth-google-oauth2
GoogleのOAuth2
を利用した認証を提供するためのGem。ユーザーがGoogleアカウントでサインインできるようにする。
omniauth-rails_csrf_protection
RailsでOmniAuth
を使用する際のCSRF(Cross-Site Request Forgery)
攻撃からアプリケーションを保護するためのGem。セキュリティを強化する。
omniauth用のカラムを追加
$ bin/rails g migration AddOmniauthToUsers provider:string uid:string
$ bin/rails g migration AddIndexUidAndProviderToUsers
追加されるカラム(AddOmniauthToUsers provider:string uid:string)
usersテーブルにprovider
とuid
の2つのカラムが追加されます
t.string "provider"
t.string "uid"
カラムに一意の制約(AddIndexUidAndProviderToUsers)
usersテーブルのuid
とprovider
に一意の制約
を設定する
t.index ["uid", "provider"], name: "index_users_on_uid_and_provider", unique: true
ファイルへの記載
.env に環境変数を記載する(隠しファイル)
・作成したクライアントID
とクライアントシークレット
を.env
に記載する
GOOGLE_CLIENT_ID=クライアントID
GOOGLE_CLIENT_SECRET=クライアントシークレット
config/initializers/devise.rb にOmniAuthの設定を記載する
config.omniauth :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET']
役割
・config.omniauth
deviseにOmniAuthの設定を追加する。OmniAuthを使って外部の認証プロバイダと連携する。
・google_oauth2
OmniAuthでGoogleのOAuth2プロバイダを指定する設定。この設定により、Googleアカウントを使ったOAuth2認証
が可能になる。
・ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET']
環境変数の読み込み。コードのセキュリティと保守性が向上する。
routes.rb にルーティングの設定をする
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
OmniAuth
の認証が成功したときに、コールバックとしてUsers::OmniauthCallbacksController
が呼び出されるようにする設定
(devise_for :users
はdeviseの実装時に記載されたもの)
omniauth_callbacks_controller.rb 記載をする
・app/controllers/users/
にomniauth_callbacks_controller.rb
を作成する
・以下の様に記載する
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
skip_before_action :verify_authenticity_token, only: :google_oauth2
def google_oauth2
callback_for(:google)
end
def callback_for(provider)
@user = User.from_omniauth(request.env['omniauth.auth'])
if @user.persisted?
sign_in_and_redirect @user, event: :authentication
set_flash_message(:notice, :success, kind: provider.to_s.capitalize) if is_navigational_format?
else
session["devise.#{provider}_data"] = request.env['omniauth.auth'].except(:extra)
redirect_to new_user_registration_url
end
end
def failure
redirect_to root_path
end
end
user.rb に記載する
app/models/user.rbの記載コード
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:omniauthable, omniauth_providers: [:google_oauth2]
has_many :live_room
has_many :messages
validates :name, presence: true
validates :name, presence: true, uniqueness: { case_sensitive: false }
has_one_attached :avatar
attr_accessor :remove_avatar
validates :uid, presence: true, uniqueness: { scope: :provider }, if: -> { uid.present? }
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.name = auth.info.name
user.email = auth.info.email
user.password = Devise.friendly_token[0, 20]
end
end
def self.create_unique_string
SecureRandom.uuid
end
end
・モジュールの記載(deviseのモジュールが記載されているので、その後ろに追記する)
:omniauthable, omniauth_providers: [:google_oauth2]
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:omniauthable, omniauth_providers: [:google_oauth2]
:omniauthable, omniauth_providers: [:google_oauth2]
・:omniauthable
deviseにOmniAuthを統合し、外部のOAuthプロバイダーを使用したユーザー認証を可能にします。
・omniauth_providers: [:google_oauth2]
使用するOmniAuthプロバイダーを指定する。
・from_omniauthメソッド
の記載
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.name = auth.info.name
user.email = auth.info.email
user.password = Devise.friendly_token[0, 20]
end
end
from_omniauthメソッドについて
・provider
とuid
を使ってユーザーを検索し、存在しなければ新規作成する。
・新規作成時にはname
、email
、password
を設定している。
・バリデーションの記載
validates :uid, presence: true, uniqueness: { scope: :provider }, if: -> { uid.present? }
:uid, presence: true, uniqueness: { scope: :provider }, if: -> { uid.present? }
・uid
が存在する場合のみ、その一意性をprovider
のスコープ内で確認する
[例]
『provider: 'google',
uid: '12345'
のユーザーが既に存在する場合』
・provider: 'google'
, uid: '12345'
の新しいユーザーを作成しようとすると、
一意性のバリデーションに失敗します。
・provider: 'facebook'
, uid: '12345'
の新しいユーザーを作成することは可能です。
・create_unique_stringメソッド
の記載
def self.create_unique_string
SecureRandom.uuid
end
create_unique_stringメソッドについて
・ユニークな文字列(UUID
)を生成する。
・UUID
は一意性が高いため、ユニークな識別子が必要な場合に使われる。
registrations_controller.rb を記載する
・app/controllers/users/
にregistrations_controller.rb
を作成する
・以下の様に記載する
class Users::RegistrationsController < Devise::RegistrationsController
def build_resource(hash = {})
hash[:uid] = User.create_unique_string
super
end
def update_resource(resource, params)
return super if params['password'].present?
resource.update_without_password(params.except('current_password'))
end
end
Googleログインで失敗したときにエラーメッセージを出すための追記
・omniauth_callbacks_controller.rb
の以下の箇所がエラーメッセージの記載になります
flash[:alert] = @user.errors.full_messages.to_sentence if @user.errors.any?
・viewファイルをフラッシュメッセージが表示できる様に追記
<% if flash[:alert] %>
<div class="custom-alert">
<span class="close" onclick="this.parentElement.style.display='none';">×</span>
<%= flash[:alert] %>
</div>
<% end %>
・表示されるメッセージの見た目を修正
・app/assets/stylesheets/
にcustom_alerts.css
を作成し、以下の様に記載する
.custom-alert {
padding: 15px;
background-color: #f44336; /* 赤色 */
color: white;
margin-bottom: 20px;
border-radius: 5px;
font-size: 16px;
}
.custom-alert .close {
margin-left: 15px;
color: white;
font-weight: bold;
float: right;
font-size: 22px;
line-height: 20px;
cursor: pointer;
transition: 0.3s;
}
.custom-alert .close:hover {
color: black;
}
・custom_alerts.scss
が読み込まれる様に記載する
@import 'custom_alerts';
Herokuに環境変数を記載
.env
に記載したクライアントID
とクライアントシークレット
をHeroku側にも設定する
・Herokuのダッシュボードから、設定
⇨設定変数
⇨鍵
と価値
に値を入力し追加
・鍵
と価値
に値を入力し追加
・環境変数
を記載(.env
が参照できるように正しく記載)
以上で開発環境と本番環境でのGoogleログインの実装は終了です。
Discussion