😎

Rails 7 × Googleログイン – omniauth-google-oauth2で簡単OAuth認証

2024/10/11に公開3

はじめに

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側の設定

  1. Google Cloud プロジェクトの作成
    • Google Cloud Consoleにアクセスし、新規プロジェクトを作成します。
    • プロジェクト名を入力し、「作成」をクリックします。
  1. 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 プロジェクトの設定は完了です。

Gemの導入

  1. 必要なGemを追加
    Gemfileに以下のGemを追加します。omniauth-google-oauth2は、Google OAuth2認証を行うためのOmniAuth戦略を提供するGemです。このGemはomniauthに依存しているため、omniauth-google-oauth2をインストールすると、omniauthも自動的にインストールされます。

    gem 'omniauth-google-oauth2'
    gem 'tailwindcss-rails'
    
  2. bundle installの実行

    bundle install
    
  3. Tailwind CSSをRailsプロジェクトにセットアップ

    次に、以下のコマンドでTailwind CSSをRailsプロジェクトにインストールします。これにより、app/assets/stylesheets/application.tailwind.cssファイルが自動的に作成され、Tailwind CSSを使用できるようになります。これをログインボタンのスタイリングに利用します。

    rails tailwindcss:install
    

実装ステップ

OmniAuthの設定

1. config/initializers/omniauth.rbの作成

次に、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を使用します。以下の手順で設定します。

  1. ターミナルで以下のコマンドを実行し、credentials.yml.encファイルを編集します。
    EDITOR=vim bin/rails credentials:edit -e development
    
  2. 開いたファイルに以下の内容を追加します。
    google:
      client_id: your_google_client_id
      client_secret: your_google_client_secret
    
    your_google_client_idyour_google_client_secretは、先ほどGoogle Cloud Consoleで取得した値に置き換えてください。
    これにより、credentials.yml.encファイルが暗号化され、config/master.keyまたは環境変数RAILS_MASTER_KEYを使用して復号化されます。
    credentialsを使用することで、環境変数をGitにコミットせずに安全に管理できます。また、本番環境と開発環境で異なる値を使用する場合は、config/credentials/development.yml.encconfig/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からの認証コールバックを受け取り、SessionsControllercreateアクションで処理します。
  • get 'auth/failure': 認証に失敗した場合、ルートパス(/)にリダイレクトします。
  • post 'logout': ログアウトリクエストをSessionsControllerdestroyアクションで処理し、as: 'logout'logout_pathというヘルパーメソッドを定義します。

4. Controller関連の作成

Googleログイン機能を実装するために、以下のControllerとHelperを作成・修正します。

  1. 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メソッドはユーザーをログアウト状態にします。
  2. ApplicationControllerの修正
    次に、SessionsHelperApplicationControllerにインクルードし、ログインを強制するための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メソッドは、ログインしていないユーザーがアクセスした場合、ルートパスにリダイレクトし、エラーメッセージを表示します。

  3. 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ログイン機能が正常に動作し、ユーザーのセッション管理が適切に行われます。

ビューの実装

  1. ログインボタンの追加
    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ブランディングガイドラインの遵守が重要です。このガイドラインに従うことで、ユーザーに一貫した視覚的体験を提供し、信頼性を高めることができます。

    主な注意点:

    1. ロゴ使用:提供された正確なGoogleロゴを使用し、色や形状を変更しないこと。
    2. ボタンデザイン:推奨されるボタンのデザイン、サイズ、余白に従うこと。
    3. テキスト:「Login with Google」や「Sign in with Google」など、推奨されるテキストを使用すること。
      ガイドラインは時々更新されるため、最新の情報を確認することをお勧めします。

動作確認

  1. サーバーの起動

    rails server
    
  2. 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リクエストでのログイン導入や、セキュリティの強化も行っていけるでしょう。この記事が皆さんの開発に役立てば幸いです!

実装に関してさらに詳しく知りたい場合や最新情報を確認したい場合は、以下の公式ドキュメントもご参照ください。

https://zenn.dev/batacon/articles/e9b4a88ede2889

参考リンク

合同会社春秋テックブログ

Discussion

ritouritou

auth_hashメソッドではrequest.env['omniauth.auth']でOmniAuth認証プロセスの一部として、リクエスト環境変数から認証情報を取得します。この情報には、ユーザーの認証に関するデータ(例:メールアドレス、名前、プロバイダー情報など)が含まれています。

find_or_create_from_auth_hashメソッドでは、認証情報からユーザーを検索し、存在しない場合は新規作成します。

User.find_or_create_by(email: email) do |user|
    user.update(email: email)
end

認証情報から既存ユーザーを参照する際は、メールアドレスではなくここではプロバイダの識別子(Google)とプロバイダから受け取ったユーザー識別子を利用するのが鉄則です。

このコードがどういう経緯で描かれたのかわからないですが、本来は上記のように参照してユーザーが見つかったとき、前回の処理の後にプロバイダ側でemailが変更されていた場合を考慮してuser.update(email: email) をすることで追従できる、という処理になっているように見えます。

この辺りについて記事を書いていますので、お時間があったら読んでみてください。

https://zenn.dev/ritou/articles/ca7be3f329e68f

arataiaratai

コメントありがとうございます!記事も読ませていただきました。プロバイダの識別子とuidを使ったユーザー参照のアドバイス、大変参考になります。確かに、メールアドレスよりもproviderとuidを使用することで、認証の一意性がより保証されますね。

今回の実装では、Googleログインのみを導入し、テーブルにはemailのみを保持するミニマムな構成を採用していたのでこのような実装にした経緯があります。今後、ご指摘を踏まえて改善を検討していきます。貴重なご指摘、ありがとうございました!

arataiaratai

ご指摘を反映するため、今後はusersテーブルにproviderとuidのカラムを追加し、ユーザーの一意性を保証する方向でコードを修正していく予定です。引き続きアドバイスを参考にさせていただきます🙏