7
10

More than 1 year has passed since last update.

【RailsAPI】devise_token_auth にてゲストログイン機能を実装したい!

Last updated at Posted at 2022-02-11

個人開発にて実装した、devise_token_authを使用したゲストログイン機能のmemoになります。

課題

  • ゲストログイン機能を実装したい

前提

  • devise_token_auth 導入済

1.Rails

1 ) ルーティングの実装

今回、ゲストログイン処理は sessions_controller.rbに追加しました。

config/routes.rb
Rails.application.routes.draw do

  namespace :v1 do
    mount_devise_token_auth_for "User", at: "auth", controllers: {
      registrations: "v1/auth/registrations",
      sessions: "v1/auth/sessions",
      confirmations: "v1/auth/confirmations",
      passwords: "v1/auth/passwords"
    }

  ######################## こちらを追加    ########################

  devise_scope :v1_user do
    post "auth/guest_sign_in", to: "auth/sessions#guest_sign_in"
  end

  ######################## こちらを追加    ########################

end

前回書いた記事で苦戦した部分になります 。
(こちらの方が今回の実装より時間を使っていますw)

2 ) ゲストユーザー情報を取得するクラスメソッドを実装

self.guest メソッドにて、ゲストユーザーを取得します。

app/controllers/v1/auth/sessions_controller.rb
class User < ApplicationRecord
  # ゲストユーザーが存在しない場合、ゲストユーザーを作成
  def self.guest
    find_or_create_by!(email: "guest@example.com") do |user|
      user.password = SecureRandom.urlsafe_base64
      user.name = "ゲストユーザー"
    end
  end

end

find_or_create_by! を使用して、ゲストユーザー(メールアドレス guest@example.com を持つユーザー)が

  • 存在する場合は、そのゲストユーザー情報を取得
  • 存在しない場合は作成

という挙動になっています。

(これに加えて guest@example.com に対して制限(新規作成・削除や編集等)をかける必要があると思いますが、今回は割愛します。)

3) ゲストログイン処理を実装する

今回のメインとなるゲストログインになります。

流れとしては以下のイメージです。

①ゲストユーザー情報を取得 → ②認証情報を作成 → ③認証情報を保存 → ④認証情報を持つゲストログインユーザー情報を返す

app/controllers/v1/auth/sessions_controller.rb
class V1::Auth::SessionsController < DeviseTokenAuth::SessionsController
  # ゲストユーザーでログイン  
  def guest_sign_in
    @resource = User.guest # ①
    @token = @resource.create_token  # ②
    @resource.save! # ③
    render_create_success # ④
  end
end

①ゲストユーザー情報を取得

先ほど作成した self.guest を使用して、ユーザー情報をインスタンス変数 @resource に格納します。

なぜ @resource に格納しているのか? というと、

devise_token_auth で @resource というインスタンス変数にユーザー情報を格納して使用するメソッドがあるためです。(後半にそのコードを記載しています。)

そのため、今回は @resource に格納しております。

②ゲストユーザーの認証情報を作成

@resource に付与する認証情報を作成します。

実行前と後で挙動を確認すると分かりやすいです。

# メソッド実行前
> @resource.tokens
=> {}

# メソッド実行
> @resource.create_token
=> #<struct DeviseTokenAuth::TokenFactory::Token
 client="f_zh3AczlwV8MsknLTtBtw",
 token="tU13YCRWvOyW7M-mXcGvUQ",
 token_hash=
  "$2a$04$HRvcYJVeIjUm.IXdUTWoDOwMZ9/4Ktx9.LiLazhNaUCMoZDkpQP26",
 expiry=1645149284>

# メソッド実行後 #=> 認証情報が入っていることが確認できる!
> @resource.tokens
=> {"f_zh3AczlwV8MsknLTtBtw"=>
  {:token=>
    "$2a$04$HRvcYJVeIjUm.IXdUTWoDOwMZ9/4Ktx9.LiLazhNaUCMoZDkpQP26",
   :expiry=>1645149284}}

③作成した認証情報を保存

上記で作成した認証情報をDBに保存します。

> @resource.save!
=> true

# 保存前
User.find_by(email: "guest@example.com").tokens
=> {}

# 保存後
User.find_by(email: "guest@example.com").tokens
=> {"Zxy1nUgAIu0VAXw5vpI3KA"=>{"token"=>"$2a$04$opeH9EQuW6j6w2YwbuNAhuJoLSKwtwNRh1vlQNLVyfiylMBo6QL8y", "expiry"=>1645151486}}

これで、ゲストユーザーに認証情報を付与することができました。

後は、保存している認証情報を返せばOKです。

④render_create_successメソッドでレスポンスを返す

あとは、認証情報を付与されたユーザー情報をレスポンスで返します。

> render_create_success
=> "{\"data\":{\"id\":3035,\"provider\":\"email\",\"uid\":\"guest@example.com\",\"allow_password_change\":false,\"name\":\"ゲストユーザー\",\"image\":null,\"email\":\"guest@example.com\"}}"

render_create_successってなに?

devise_token_authで定義されており、詳細が以下になります。
(こちらが先ほどの@resource を使用するメソッドとなります。)

def render_create_success
    render json: {
      data: resource_data(resource_json: @resource.token_validation_response)
    }
end

def resource_data(opts = {})
  response_data = opts[:resource_json] || @resource.as_json
  response_data['type'] = @resource.class.name.parameterize if json_api?
  response_data
end

def token_validation_response
  as_json(except: %i[tokens created_at updated_at])
end

4 ) テストの実装

後はリクエストを送信した時、期待している情報が返ってくることをテストしておきます。(この辺は設定によって違ってくると思います。

認証情報がheaderに格納されていればOKです。

spec/requests/v1/auth/sessions_spec.rb
spec/requests/v1/auth/sessions_spec.rb

    # ゲストログイン
    describe "POST v1/users/auth/guest_sign_in" do
      context "リクエストが送信された時" do
        subject(:request) { post(v1_auth_guest_sign_in_path) }

        it "ゲストログインできる" do
          request
          expect(response).to have_http_status(:ok)
          header = response.header
          expect(header["access-token"]).to be_present
          expect(header["client"]).to be_present
          expect(header["expiry"]).to be_present
          expect(header["uid"]).to be_present
          expect(header["token-type"]).to be_present
        end
      end
    end

これでゲストログイン機能は実装できました。

2.React

あとは、React側はリクエスト処理を送信し、認証情報を保存することで、ゲストログインが実現できます。

( こちらはサンプルです。参考例として記載しています。)

const GuestlogIn = useCallback(() => {
    axios
      .post(http://localhost:5000/v1/auth/guest_sign_in)
      .then((res) => {
      // 成功処理
      // Cookieに認証情報を保存
      // 画面遷移
      toast.success('ゲストログインしました');  // メッセージ
      })
      .catch((error) => {
       // 例外処理
      toast.error('ゲストログインできませんでした'); // メッセージ
      });
  }, []);
  return { GuestlogIn };
};

まとめ

今回はゲストログインを実装することができました。

GitHubを見て、なぜそうなるのか動作確認しながら確認することが重要ですね。

次回はメール認証をやりたいと思います。

参考資料

7
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
10