はじめに
さまざまな記事を拝見して、Google認証は設定できましたが、備忘録として今回記事を作成いたします。基本的には公式を参考に作成していますが、書かれていることをそのまま記述するのではなく、調べて理解したことを記述しております。間違い等ございましたら、教えていただきますようお願いいたします。少しでもお役に立てれば幸いです。
現状
技術スタック
OS: macOS Sonoma 14.6.1
Docker: 24.0.2
Docker Compose: v2.18.1
Ruby: 3.1.4
Rails: 7.0.8.1
データベース: PostgreSQL 12.18
CSSフレームワーク: Bootstrap 5
CircleCI: 継続的インテグレーション(CI/CD)を導入
- Gemのdeviseについては導入していましたが、既にUserモデルを作成してしまっていたので、こちらを参考に設定しました。
上記の記事で一点私と異なった点が、rails db:migrateでエラーが起こったとなっていたが、私の場合は以下のコマンドでエラーが出ず実行できた。
docker-compose exec web rails db:migrate
手順
1 Google認証情報の設定
これはGoogleの開発者用サイトで、Googleログインを使用するために設定するものです。そのために、私は以下のサイトを参考にしました。
こちらのサイトから設定を行い、APIキーを取得してください。
2 Gemのインストール及びカラムの追加
下記のGemを追加してください。
gem 'devise'
gem 'omniauth-google-oauth2'
gem 'omniauth-rails_csrf_protection'
以下の2つのカラムを追加してください。
rails g migration AddOmniauthToUsers provider:string uid:string
docker-compose exec コンテナ名 rails db:migrate
これらのカラムはprovider
はどの外部認証活用したかを保存し、uid
はそれらのサービスでのIDを保存しておく。それらによって、すでにデータがあればログインでき、なければ新規ユーザーとしてデータを保存する。
3 プロバイダーの設定
先ほど作成したGoogleログインを使用するために作成したAPIキーを活用して、紐づける役割だと思います、、、、、(自信がないので違っていれば教えてください)
config.omniauth :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET']
4 モデルに外部認証の設定を追加
devise :omniauthable, omniauth_providers: [:google_oauth2]
上記の設定は、deviseにomniauthable
を追加して、外部認証を有効にします。omniauth_providers: [:google_oauth2]
はどの外部認証サービスを活用するかを明確にします。今回はGoogleログインを使用する設定です。
これにより、config/routes.rbにてevise_for :usersを記述することで、google関連の以下のルーティングも自動作成される。
user_google_oauth2_omniauth_authorize GET|POST /users/auth/google_oauth2(.:format) devise/omniauth_callbacks#passthru
user_google_oauth2_omniauth_callback GET|POST /users/auth/google_oauth2/callback(.:format) devise/omniauth_callbacks#google_oauth2
5 ルーティングの定義
ログアウトのルーティングには名前付きルートヘルパーを活用して、link_to
にてmethod: :delete
を省略することもできますが、今回はdevise_scopeにてそれらの設定は行いません。
devise_for :users, controllers: {
omniauth_callbacks: 'users/omniauth_callbacks'
}
devise_for :users
の部分では、deviseが提供する認証機能のルートを自動生成してくれます。ですので、先ほどのモデルの設定があることで必要なルートが作成されます。
Googleからの認証結果が、/users/auth/google_oauth2/callback
に送られるタイミングでdeviseがそのリクエストを検知するらしい。もう少し簡単に言うと、ルートにリクエストが来たタイミングで、controllers:
が参照されるらしいです。
ここで疑問に思ったことは、ルートにリクエストが来たタイミングならログインボタンを押したタイミングでも参照されるのでは?と疑問に思いました。結論、そのタイミングは参照されない様子。ログインボタンを押したタイミングはユーザーをリダイレクトするので、リクエストされるのはユーザーが認証を完了してGoogleが認証結果をアプリのURLに送られた時のみ。つまり、アプリからGoogleにリクエストする時に'controllers'は参照されずに、Googleからアプリにリクエストする時に参照されるらしいです。難しいですが、少し理解ができました。
6 コントローラーの設定
まずapp/controllers/users/omniauth_callbacks_controller.rb
を作成します。
bin/rails g controller users::omniauth_callbacks
作成できたら、omniauth_callbacks_controller.rb
を編集していきます。
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
skip_before_action :verify_authenticity_token, only: :google_oauth2
def google_oauth2
@user = User.from_omniauth(request.env['omniauth.auth'])
if @user.persisted?
sign_in_and_redirect @user, event: :authentication
set_flash_message(:notice, :success, kind: "Google") if is_navigational_format?
else
session["devise.google_data"] = request.env["omniauth.auth"].except(:extra)
redirect_to new_user_registration_url
end
end
def failure
redirect_to root_path
end
end
skip_before_action :verify_authenticity_token, only: :google_oauth2
上記の記述は、railsはCSRFトークンを使用して、フォーム送信やログインリクエストが正しいかチェックしているらしい。ただし、外部認証だとCSRFトークンが含まれていないため、google_oauth2
のCSRFトークンのチェックを無効にしているらしいです。
def google_oauth2
@user = User.from_omniauth(request.env['omniauth.auth'])
if @user.persisted?
sign_in_and_redirect @user, event: :authentication
set_flash_message(:notice, :success, kind: "Google") if is_navigational_format?
else
session["devise.google_data"] = request.env["omniauth.auth"].except(:extra)
redirect_to new_user_registration_url
end
end
上記の記述は認証結果を処理するための記述です。リクエストが送られたら上記のメソッドが呼び出されます。User.from_omniauth
メソッドは認証情報を検索し、見つからなければ作成します。なので、基本的にはif @user.persisted?
ではtrue
になるはずですが、Googleからの情報にメールアドレスがないなどの場合には、バリデーションエラーとなって、else
の処理になります。
7 モデルの追記
先ほどのUser.from_omniauth
メソッドを追記して、ユーザーの情報を取得・保存する処理を記述する。
def self.from_omniauth(auth)
find_or_create_by(provider: auth.provider, uid: auth.uid) do |user|
user.email = auth.info.email
user.password = Devise.friendly_token[0, 20]
user.name = auth.info.name
end
end
8 ログイン・ログアウトボタンの設置
<%= button_to "グーグルでのログイン", user_google_oauth2_omniauth_authorize_path, method: :post, data: { turbo: false } %>
link_to
で実装した場合、エラーが出てしまったので、一旦はこちらの記述で実装しております。
おわりに
今回のGoogle認証は初学者の私にとってかなり難しかったですが、色々なコードを見て学ぶ勉強になりました。今はGoogle認証だけですが、時間があれば他の認証も追加していきたいと思います。
以下、参考にさせていただいた記事等を載せておきます。最後まで閲覧いただき誠にありがとうございました。
↑公式とこちらのサイトを見ながら実装いたしました。大変お世話になりました。