📚

Googleログインの実装(devise + omniauth)

2024/07/22に公開

開発環境

  • macOS
  • VSCode
  • Rails 7.1.3.3
  • ruby-3.2.3
  • PostgreSQL 16.2

行いたいこと

  • deviseによるログイン機能の実装に、Googleログインの実装を追加する。

前提として

  • 既にdeviseによるログイン機能の実装ができている
  • Herokuにはデプロイ済み





GoogleのクライアントIDとクライアントシークレットの取得

・こちらのリンクから
https://zenn.dev/tteaoocl/articles/5601b28fa8045d

Gemのインストール

Gemfile
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テーブルにprovideruidの2つのカラムが追加されます

db/schema.rb
t.string "provider"
t.string "uid"
カラムに一意の制約(AddIndexUidAndProviderToUsers)

usersテーブルのuidprovider一意の制約を設定する

db/schema.rb
t.index ["uid", "provider"], name: "index_users_on_uid_and_provider", unique: true

ファイルへの記載

.env に環境変数を記載する(隠しファイル)

・作成したクライアントIDクライアントシークレット.envに記載する

.env
GOOGLE_CLIENT_ID=クライアントID
GOOGLE_CLIENT_SECRET=クライアントシークレット

config/initializers/devise.rb にOmniAuthの設定を記載する

config/initializers/devise.rb
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 にルーティングの設定をする

config/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を作成する
・以下の様に記載する

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の記載コード
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]

app/models/user.rb
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メソッドの記載

app/models/user.rb
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メソッドについて

provideruidを使ってユーザーを検索し、存在しなければ新規作成する。
・新規作成時にはnameemailpasswordを設定している。

・バリデーションの記載

app/models/user.rb
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メソッドの記載

app/models/user.rb
def self.create_unique_string
    SecureRandom.uuid
  end
create_unique_stringメソッドについて

・ユニークな文字列(UUID)を生成する。
UUIDは一意性が高いため、ユニークな識別子が必要な場合に使われる。

registrations_controller.rb を記載する

app/controllers/users/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の以下の箇所がエラーメッセージの記載になります

app/controllers/users/omniauth_callbacks_controller.rb
flash[:alert] = @user.errors.full_messages.to_sentence if @user.errors.any?

・viewファイルをフラッシュメッセージが表示できる様に追記

app/views/devise/registrations/new.html.erb
<% if flash[:alert] %>
  <div class="custom-alert">
    <span class="close" onclick="this.parentElement.style.display='none';">&times;</span>
    <%= flash[:alert] %>
  </div>
<% end %>

・表示されるメッセージの見た目を修正

表示例

Image from Gyazo

app/assets/stylesheets/custom_alerts.cssを作成し、以下の様に記載する

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が読み込まれる様に記載する

app/assets/stylesheets/application.bootstrap.scss
@import 'custom_alerts';

Herokuに環境変数を記載

.envに記載したクライアントIDクライアントシークレットをHeroku側にも設定する
・Herokuのダッシュボードから、設定設定変数 価値に値を入力し追加
Image from Gyazo
価値に値を入力し追加
Image from Gyazo
環境変数を記載(.envが参照できるように正しく記載)
Image from Gyazo

以上で開発環境と本番環境でのGoogleログインの実装は終了です。






GitHubで編集を提案

Discussion