providerが”email”の場合、uidにメールアドレスが入り、変更時にuidも更新されます。
uidは認証に使っているので、メールアドレス変更(メールアドレス確認のURLにアクセスした)直後にフロントがログイン状態だとuid変更に気付けないので、認証に失敗します。
確認のURLにパラメータ渡せたと思いますが、パラメータで渡したくないのと、別端末や別ブラウザだと通知できないので、uidが不変の値になるようにidに変更しました。
また、ローカルストレージ(Vuexストアも)に個人情報(今回はメールアドレス)を入れていますが、極力、保持したくないので、レスポンスから外して、登録情報詳細APIを別途、作成しました。

方針

DBのuidの値は変更したくない。運用後に変更する場合に手間が大きい。
override出来ないかGemのソースを追いましたが、影響範囲が大きいので、prepend_before_actionとafter_action使って、リクエストの最初と最後にヘッダを書き換える事にしました。

idは個人情報(個人を特定できるのでは?)との疑問に思うかもしれませんが、そもそもidはAPIでは渡さない方が良い。
将来のリプレイスで、idに依存して制約が増えてしまうので、あくまでもバックエンド(Rails)の中だけに留めておいた方が良く、識別が必要な場合はユニークなkeyやcodeを用意する。
一時的だったり、変わっても良いものは、無理せずidでもOKかと。

実装修正

app/controllers/application_controller.rb を変更

class ApplicationController < ActionController::Base
+  after_action :update_response_uid_header

  private

+  # リクエストのuidヘッダを[id+36**2](36進数)からuidに変更 # Tips: uidがメールアドレスだと、メールアドレス確認後に認証に失敗する為
+  def update_request_uid_header
+    return if request.headers['uid'].blank?
+
+    user = User.find_by(id: request.headers['uid'].to_i(36) - (36**2))
+    request.headers['uid'] = user&.uid
+  end
+
+  # レスポンスのuidヘッダをuidから[id+36**2](36進数)に変更
+  def update_response_uid_header
+    return if response.headers['uid'].blank?
+
+    user = User.find_by(uid: response.headers['uid'])
+    response.headers['uid'] = user.present? ? (user.id + (36**2)).to_s(36) : nil
+  end

暗号化も考えましたが、意味のない数字にそこまでしてもと思ったので、36進数に変換しました。

> 1.to_s(36)
 => "1" 
> 10.to_s(36)
 => "a" 
> 35.to_s(36)
 => "z" 

35まで1桁なのも微妙なので、36**2を加算して、3桁以上になるようにしました。

> (1 + 36**2).to_s(36)
 => "101" 
> (10 + 36**2).to_s(36)
 => "10a" 
> (35 + 36**2).to_s(36)
 => "10z" 

app/controllers/application_auth_controller.rb を変更
(Devise Token Auth以外のAPIの継承元)

class ApplicationAuthController < ApplicationController
  include DeviseTokenAuth::Concerns::SetUserByToken
  skip_before_action :verify_authenticity_token, if: :format_api?
  prepend_before_action :not_acceptable_response_not_api_accept, if: :format_api?
+  prepend_before_action :update_request_uid_header
  before_action :standard_devise_support

app/controllers/users/auth/sessions_controller.rb を変更

class Users::Auth::SessionsController < DeviseTokenAuth::SessionsController
  include DeviseTokenAuth::Concerns::SetUserByToken
  skip_before_action :verify_authenticity_token
  prepend_before_action :unauthenticated_response_sign_out, only: %i[destroy], unless: :user_signed_in?
  prepend_before_action :not_acceptable_response_not_api_accept
+  prepend_before_action :update_request_uid_header

他のprepend_before_actionよりも先に実行する為、prepend_before_actionの最後に追加する。
最初に追加するとuser_signed_in?が常にfalseになってしまいます。
理由は、prepend_before_actionがbefore_actionリストの先頭に追加される為で、後に記載した方が先に実行されるから。

ApplicationControllerで1回だけ定義にしたかったのですが、継承元の方が先にリストに入るので、個別に定義するようにしました。
after_actionは後ろに追加で、厳密には最後ではないですが、他のactionに影響しないので、ApplicationControllerで1回だけ定義しています。

同様に下記にも追加
app/controllers/users/auth/confirmations_controller.rb
app/controllers/users/auth/passwords_controller.rb
app/controllers/users/auth/registrations_controller.rb
app/controllers/users/auth/token_validations_controller.rb
app/controllers/users/auth/unlocks_controller.rb

Spec修正

※作成済みのSpecに依存した差分になっていますので、参考までに。

spec/support/user_contexts.rb を変更

shared_context 'APIログイン処理' do |target = :user, use_image = false|
  include_context 'ユーザー作成', target, use_image
  let(:auth_token) { user.create_new_auth_token }
  let(:auth_headers) do
    {
-      'uid' => auth_token['uid'],
+      'uid' => (user.id + (36**2)).to_s(36),
      'client' => auth_token['client'],
      'access-token' => auth_token['access-token']
    }
  end
end
  let(:expect_exist_auth_header) do
-    expect(response.header['uid']).to eq(current_user.email)
+    expect(response.header['uid']).to eq((current_user.id + (36**2)).to_s(36))
    expect(response.header['client']).not_to be_nil
    expect(response.header['access-token']).not_to be_nil # Tips: 一定時間内のリクエスト(batch_request)は半角スペースが入る
    expect(response.header['expiry']).not_to be_nil # Tips: 同上
  end

今回のコミット内容
※追加実装した登録情報詳細API(TODO: パスワード認証追加)も含まれています。
https://dev.azure.com/nightonly/rails-app-origin/_git/rails-app-origin/commit/e7b79abc264177a5cf90155ecac0b314220adfa3

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です