個人開発にて実装した、devise_token_authを使用したゲストログイン機能のmemoになります。
課題
- ゲストログイン機能を実装したい
前提
- devise_token_auth 導入済
1.Rails
1 ) ルーティングの実装
今回、ゲストログイン処理は sessions_controller.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
メソッドにて、ゲストユーザーを取得します。
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) ゲストログイン処理を実装する
今回のメインとなるゲストログインになります。
流れとしては以下のイメージです。
①ゲストユーザー情報を取得 → ②認証情報を作成 → ③認証情報を保存 → ④認証情報を持つゲストログインユーザー情報を返す
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
# ゲストログイン
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を見て、なぜそうなるのか動作確認しながら確認することが重要ですね。
次回はメール認証をやりたいと思います。
参考資料