Rails 7 × Googleログイン – omniauth-google-oauth2で簡単OAuth認証
はじめに
GoogleログインをWebアプリケーションに導入することで、ユーザーは煩わしい新規登録やパスワード管理をせずに、簡単にアカウントを作成・ログインできます。今回は、Rails 7において、omniauth-google-oauth2を使ってGoogleログインを実装する手順を紹介します。この実装では、開発環境のシンプルさを重視し、認証リクエストにはGETメソッドを使用しています。
補足:セキュリティやユーザーエクスペリエンスの観点から、本番環境ではPOSTリクエストが推奨されます。
この記事では、Google Cloudでの設定から、Railsアプリケーションでの具体的なコーディングまでをステップごとに解説していきます。
使用しているRuby/Railsやgemのバージョンは以下の通りです。
- Ruby 3.3.0
- Rails 7.3.1.2
- omniauth 2.1.2
- omniauth-google-oauth2 1.1.1
- tailwindcss-rails 2.3.0
準備
Google Cloud側の設定
-
Google Cloud プロジェクトの作成
-
Google Cloud Consoleにアクセスし、新規プロジェクトを作成します。
- プロジェクト名を入力し、「作成」をクリックします。
-
Google Cloud Consoleにアクセスし、新規プロジェクトを作成します。
-
OAuth 2.0認証情報の取得
- Google Cloud Consoleの左側メニューから「APIとサービス」→「認証情報」を選択。
- 「認証情報を作成」→「OAuth 2.0 クライアントID」を選択します。
- 「同意画面の設定」でUserTypeは「外部」を選択しアプリケーション名やサポートメールなどの必要項目を入力し、「保存」をクリックします。
- 今回はOAuthのスコープとして「Googleアカウントの主なメールアドレスの取得」を設定します。その後、「保存して次へ」をクリックして次に進みます。
- テスト用として、自分のメールアドレスを使い、アプリで認証を試行するユーザーとして登録します。
- 同意画面の設定が完了したら、「APIとサービス」から「認証情報」を選択し、「認証情報を作成」→「OAuthクライアントID」を選んで、クライアントIDとクライアントシークレットの作成を開始します。
補足: 認証情報を作成する際のリダイレクトURIは、
http://localhost:3000/auth/google_oauth2/callback
を指定しています。コールバックを受け取るURIを指定してください。- クライアントIDとクライアントシークレットが表示されますので、これを後で使うために控えておきます。
- これでGoogle Cloud プロジェクトの設定は完了です。
- Google Cloud Consoleの左側メニューから「APIとサービス」→「認証情報」を選択。
Gemの導入
-
必要なGemを追加
Gemfile
に以下のGemを追加します。omniauth-google-oauth2
は、Google OAuth2認証を行うためのOmniAuth戦略を提供するGemです。このGemはomniauth
に依存しているため、omniauth-google-oauth2
をインストールすると、omniauth
も自動的にインストールされます。gem 'omniauth-google-oauth2' gem 'tailwindcss-rails'
-
bundle installの実行
bundle install
-
Tailwind CSSをRailsプロジェクトにセットアップ
次に、以下のコマンドでTailwind CSSをRailsプロジェクトにインストールします。これにより、
app/assets/stylesheets/application.tailwind.css
ファイルが自動的に作成され、Tailwind CSSを使用できるようになります。これをログインボタンのスタイリングに利用します。rails tailwindcss:install
実装ステップ
OmniAuthの設定
config/initializers/omniauth.rb
の作成
1. 次に、omniauth-google-oauth2
の設定を行います。以下の内容で新規ファイルを作成します。
# config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2,
Rails.application.credentials.google[:client_id],
Rails.application.credentials.google[:client_secret]
end
OmniAuth.config.allowed_request_methods = %i[get]
OmniAuth.config.allowed_request_methods = %i[get]
は、セキュリティ設定の一環で、GETリクエストのみを許可しています。ただし、CSRF攻撃を防ぐために、POSTリクエストを使用する方が一般的です。開発環境では簡便さを重視してGETメソッドを許可していますが、本番環境ではPOSTリクエストを推奨されます。
2. クレデンシャルの設定
クライアントIDとシークレットを安全に管理するために、Railsのcredentialsを使用します。以下の手順で設定します。
- ターミナルで以下のコマンドを実行し、credentials.yml.encファイルを編集します。
EDITOR=vim bin/rails credentials:edit -e development
- 開いたファイルに以下の内容を追加します。
google: client_id: your_google_client_id client_secret: your_google_client_secret
your_google_client_id
とyour_google_client_secret
は、先ほどGoogle Cloud Consoleで取得した値に置き換えてください。
これにより、credentials.yml.enc
ファイルが暗号化され、config/master.key
または環境変数RAILS_MASTER_KEY
を使用して復号化されます。
credentialsを使用することで、環境変数をGitにコミットせずに安全に管理できます。また、本番環境と開発環境で異なる値を使用する場合は、config/credentials/development.yml.enc
やconfig/credentials/production.yml.enc
を作成することで環境ごとに異なる値を設定できます。
3. ルーティングの設定
config/routes.rb
にGoogleログインのルートを追加します。
Rails.application.routes.draw do
get 'auth/:provider/callback', to: 'sessions#create'
get 'auth/failure', to: redirect('/')
post 'logout', to: 'sessions#destroy', as: 'logout'
end
-
get 'auth/:provider/callback'
: Googleからの認証コールバックを受け取り、SessionsController
のcreate
アクションで処理します。 -
get 'auth/failure'
: 認証に失敗した場合、ルートパス(/
)にリダイレクトします。 -
post 'logout'
: ログアウトリクエストをSessionsController
のdestroy
アクションで処理し、as: 'logout'
でlogout_path
というヘルパーメソッドを定義します。
Controller
関連の作成
4. Googleログイン機能を実装するために、以下のControllerとHelperを作成・修正します。
-
SessionsHelperの作成
まず、SessionsHelper
を作成して、ログイン状態の管理に必要なメソッドを定義します。# app/helpers/sessions_helper.rb module SessionsHelper def current_user return unless user_id = session[:user_id] @current_user ||= User.find_by(id: user_id) end def log_in(user) session[:user_id] = user.id end def log_out session.delete(:user_id) @current_user = nil end end
このHelperモジュールでは、ログイン状態の管理に必要なメソッドを定義しています。
-
current_user
メソッドは現在ログインしているユーザーを返します。 -
log_in
メソッドはユーザーをログイン状態にします。 -
log_out
メソッドはユーザーをログアウト状態にします。
-
-
ApplicationControllerの修正
次に、SessionsHelper
をApplicationController
にインクルードし、ログインを強制するためのrequire_login
メソッドを設定します。# app/controllers/application_controller.rb class ApplicationController < ActionController::Base include SessionsHelper before_action :require_login def require_login return if current_user flash[:danger] = 'Googleログインが必要です' redirect_to root_path end end
require_loginメソッドは、ログインしていないユーザーがアクセスした場合、ルートパスにリダイレクトし、エラーメッセージを表示します。
-
SessionsControllerの作成
最後に、Googleログインの認証結果を処理するためのSessionsController
を作成します。# app/controllers/sessions_controller.rb class SessionsController < ApplicationController skip_before_action :require_login, only: :create def create user = find_or_create_from_auth_hash(auth_hash) log_in user if user redirect_to root_path end def destroy log_out redirect_to root_path end private def auth_hash request.env['omniauth.auth'] end def find_or_create_from_auth_hash(auth_hash) email = auth_hash['info']['email'] User.find_or_create_by(email: email) do |user| user.update(email: email) end end end
この
SessionsController
では以下の処理を行っています-
create
アクションでは、Googleからの認証情報を使ってユーザーを検索または作成し、ログイン処理を行います。 -
destroy
アクションでログアウト処理を行います。 -
auth_hash
メソッドではrequest.env['omniauth.auth']
でOmniAuth認証プロセスの一部として、リクエスト環境変数から認証情報を取得します。この情報には、ユーザーの認証に関するデータ(例:メールアドレス、名前、プロバイダー情報など)が含まれています。 -
find_or_create_from_auth_hash
メソッドでは、認証情報からユーザーを検索し、存在しない場合は新規作成します。
これらの実装により、Googleログイン機能が正常に動作し、ユーザーのセッション管理が適切に行われます。
-
ビューの実装
-
ログインボタンの追加
app/views/layouts/_header.html.erb
にGoogleログイン用のボタンを追加します。<header class="w-full mt-5 text-gray-700 bg-white border-t border-gray-100 shadow-sm body-font"> <div class="container flex items-start justify-between p-6 min-w-full"> <a href="/" class="flex items-center mb-0 font-bold text-gray-900 title-font"> My Project </a> <div class="items-center h-full"> <% if current_user %> <div class="flex"> <p class="inline-block mr-5 font-medium">LoginUser:<%= current_user.email %></p> <form action="/logout" method="post"> <button type="submit" class="mr-5 font-medium hover:text-gray-900">Logout</button> </form> </div> <% else %> <a href="/auth/google_oauth2" class="inline-block dark:bg-gray-800 px-4 py-2 border flex gap-2 border-slate-200 dark:border-slate-700 rounded-lg text-slate-700 dark:text-slate-200 hover:border-slate-400 dark:hover:border-slate-500 hover:text-slate-900 dark:hover:text-slate-300 hover:shadow transition duration-150"> <img src="https://www.svgrepo.com/show/475656/google-color.svg" alt="google logo" class="w-6 h-6" loading="lazy"> <span>Login with Google</span> </a> <% end %> </div> </div> </header>
今回の実装では、
tailwindcss-rails
を使用してスタイリングを行っています。このGemはRailsアプリに簡単にTailwind CSSを導入でき、シンプルかつ柔軟なデザインが可能です。また、ログインボタンはGoogleが公式に提供しているSVGロゴを使用しています。
補足: Googleログインボタンの実装においては、Googleブランディングガイドラインの遵守が重要です。このガイドラインに従うことで、ユーザーに一貫した視覚的体験を提供し、信頼性を高めることができます。
主な注意点:
- ロゴ使用:提供された正確なGoogleロゴを使用し、色や形状を変更しないこと。
- ボタンデザイン:推奨されるボタンのデザイン、サイズ、余白に従うこと。
- テキスト:「Login with Google」や「Sign in with Google」など、推奨されるテキストを使用すること。
ガイドラインは時々更新されるため、最新の情報を確認することをお勧めします。
動作確認
-
サーバーの起動
rails server
-
Googleログインのテスト
ブラウザでhttp://localhost:3000
にアクセスし、ログインボタンをクリックします。Googleの認証画面にリダイレクトされ、認証が完了すれば、アプリケーションに戻りユーザーがログインされた状態になります。
トラブルシューティング
リダイレクトURIの不一致エラー
redirect_uri_mismatch
のエラーが出た場合は、Google Cloud Consoleで設定したリダイレクトURIが正しいか確認してください。http://localhost:3000/auth/google_oauth2/callback
が設定されていることを確認しましょう。
認証情報のエラー
クライアントIDやクライアントシークレットが正しく設定されているか、また環境変数が正しく読み込まれているかを確認してください。
おわりに
今回は、Rails 7にomniauth-google-oauth2
を使ってGoogleログインを実装する方法を紹介しました。この機能を導入することで、ユーザーは手軽にアカウント作成・ログインができ、ユーザー体験の向上が期待できます。
さらにこの機能を発展させ、POSTリクエストでのログイン導入や、セキュリティの強化も行っていけるでしょう。この記事が皆さんの開発に役立てば幸いです!
実装に関してさらに詳しく知りたい場合や最新情報を確認したい場合は、以下の公式ドキュメントもご参照ください。
Discussion
認証情報から既存ユーザーを参照する際は、メールアドレスではなくここではプロバイダの識別子(Google)とプロバイダから受け取ったユーザー識別子を利用するのが鉄則です。
このコードがどういう経緯で描かれたのかわからないですが、本来は上記のように参照してユーザーが見つかったとき、前回の処理の後にプロバイダ側でemailが変更されていた場合を考慮して
user.update(email: email)
をすることで追従できる、という処理になっているように見えます。この辺りについて記事を書いていますので、お時間があったら読んでみてください。
コメントありがとうございます!記事も読ませていただきました。プロバイダの識別子とuidを使ったユーザー参照のアドバイス、大変参考になります。確かに、メールアドレスよりもproviderとuidを使用することで、認証の一意性がより保証されますね。
今回の実装では、Googleログインのみを導入し、テーブルにはemailのみを保持するミニマムな構成を採用していたのでこのような実装にした経緯があります。今後、ご指摘を踏まえて改善を検討していきます。貴重なご指摘、ありがとうございました!
ご指摘を反映するため、今後はusersテーブルにproviderとuidのカラムを追加し、ユーザーの一意性を保証する方向でコードを修正していく予定です。引き続きアドバイスを参考にさせていただきます🙏