🌊

Rails 7のアプリにAuth0を利用したユーザー認証を搭載

2023/01/07に公開

はじめに

私がRailsでアプリを開発する時は、Deviseでユーザー認証の機能を作成するか、OmniAuthでソーシャルログインを利用することがほとんどでした。Deviseでも適切にユーザー認証を管理していれば大丈夫だと思うのですが、将来的にMFA対応が必要となる可能性もありそうなアプリがあり、今回はAuth0を採用することにしました。

ライブラリのインストール

Gemfileに必要となるライブラリを追加して、bundleコマンドを実行してインストールします。

Gemfile
gem "omniauth-auth0"
gem "omniauth-rails_csrf_protection"
% bundle

Auth0でアプリケーションを作成して設定

Auth0でアプリケーションを作成します。アプリケーションの種類にはRegular Web Applicationsを選択します。アプリケーションが作成されたら、Allowed Callback URLshttp://localhost:3000/auth/auth0/callbackを追加しておきます。

EDITOR=vi rails credentials:editを実行してClient IDClient Secretを保存します。

auth0:
  domain: ******.**.auth0.com
  client_id: ********
  client_secret: ****************

omniauth.rbを作成します。

config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :auth0,
           Rails.application.credentials.auth0.client_id,
           Rails.application.credentials.auth0.client_secret,
           Rails.application.credentials.auth0.domain,
           callback_path: "/auth/auth0/callback",
           scope: "openid email"

  OmniAuth.config.on_failure =
    Proc.new { |env| OmniAuth::FailureEndpoint.new(env).redirect_to_failure }
end

scopeにはopenid emailを指定しました。name nickname pictureを利用する場合はprofileも指定します。

https://auth0.com/docs/get-started/apis/scopes/sample-use-cases-scopes-and-claims

ユーザーモデルとセッションコントローラーの作成

ユーザーモデル

% rails g model User provider:string uid:string email:string
app/models/User.rb
class User < ApplicationRecord
  def self.find_or_create_from_auth(auth)
    provider = auth[:provider]
    uid = auth[:uid]

    self.find_or_create_by(provider: provider, uid: uid) do |user|
      user.email = auth[:info][:email]
    end
  end
end

セッションコントローラー

% rails g controller sessions
app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
  def create
    user = User.find_or_create_from_auth(request.env["omniauth.auth"])
    session[:user_id] = user.uid
    redirect_to request.env["omniauth.origin"] || root_url
  end

  def destroy
    reset_session
    request_params = {
      returnTo: root_url,
      client_id: Rails.application.credentials.auth0.client_id,
    }
    redirect_to URI::HTTPS.build(
                  host: Rails.application.credentials.auth0.domain,
                  path: "/v2/logout",
                  query: request_params.to_query,
                ).to_s,
                allow_other_host: true
  end

  def failure
    redirect_to root_url
  end
end

ルーティングを追加します。

config/routes.rb
get "/auth/:provider/callback", to: "sessions#create"
get "/auth/failure", to: "sessions#failure"
get "/sign_out", to: "sessions#destroy"

アプリ全体で利用するメソッドの作成

application_controller.rbにメソッドを追加します。Deviseのメソッド名と同じようにしてみました。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  helper_method :current_user, :user_signed_in?

  private

  def authenticate_user!
    redirect_to root_path unless session[:user_id]
  end

  def current_user
    return unless session[:user_id]
    @current_user ||= User.find_by(uid: session[:user_id])
  end

  def user_signed_in?
    !!session[:user_id]
  end
end

動作確認

ログインとログアウトの状態に応じて表示するボタンを切り替える例です。

app/views/layouts/application.html.erb
<% if user_signed_in? %>
  <%= link_to "Sign out", sign_out_path, data: { turbo: false } %>
<% else %>
  <%= button_to "Sign in", "/auth/auth0", method: :post,
    data: { turbo: false } %>
<% end %>

before_actionauthenticate_user!を指定して、ログインしているときのみ表示する例です。

app/controllers/contents_controller.rb
class ContentsController < ApplicationController
  before_action :authenticate_user!
end

おわりに

Rails 7のアプリでAuth0を利用したユーザー認証ができるようになりました。次はAuth0のAPIを利用して、Rails側の操作に応じたAuth0のデータ管理をしていきたいと思います。

参考

https://auth0.com/docs/quickstart/webapp/rails

https://dev.classmethod.jp/articles/try_out_auth0_by_using_rails6/

https://kattya.dev/articles/2022-03-08-authentication-with-auth0-on-rails/

Discussion