CacheStore セッション削除で `options[:drop]` を正しく尊重するように修正
ActionDispatch::Session::CacheStore の delete_session が options[:drop] を無視し、常に新しいセッション ID を生成していた問題を修正し、Rack のセッション削除契約に合わせて nil を返すようにしました。これにより、ドロップされたセッションが再割り当てされる不具合が解消されます。
背景
CacheStore の delete_session は、options[:drop] が指定されても常に generate_sid を呼び出し、新しいセッション ID を返していました。その結果、セッションが削除されるべき場面でクッキーが再生成され、意図しない再認証が行われていました。 (Rack の delete_session 契約では、options[:drop] が true のときは nil を返すことが求められます)。
CookieStore では同様のメソッドが正しく実装されており、options[:drop] が設定されている場合は generate_sid を呼び出さず nil を返しています。この不整合がテストで指摘され、CacheStore でも同等の挙動が必要となりました。\
\
この修正は、セッション管理の一貫性を保ち、期待通りにセッションを破棄できるようにするためのものです。
技術的な変更
CacheStore#delete_session に条件分岐を追加し、options[:drop] が true の場合は新しいセッション ID を生成せず nil を返すようにしました。変更前後のコードは以下の通りです。
変更前:
def delete_session(env, sid, options)
@cache.delete(cache_key(sid.private_id))
@cache.delete(cache_key(sid.public_id))
generate_sid
end
変更後:
def delete_session(env, sid, options)
@cache.delete(cache_key(sid.private_id))
@cache.delete(cache_key(sid.public_id))
generate_sid unless options[:drop]
end
このシンプルな条件分岐により、options[:drop] が指定されたときは nil が返り、Rack の期待通りにセッション書き込みがスキップされます。
さらに、テストが追加されて動作を検証しています。MemoryStore を使用して CacheStore を生成し、drop: true のケースで nil が返ること、drop が無い場合は新しいセッション ID が生成されることを assert_nil と assert_not_nil で確認しています。
def test_delete_session_returns_nil_when_dropped
cache = ActiveSupport::Cache::MemoryStore.new
store = ActionDispatch::Session::CacheStore.new(->(env) { [200, {}, []] }, cache: cache)
sid = Rack::Session::SessionId.new("0" * 32)
# With options[:drop], delete_session must return nil so the session is
# dropped rather than reassigned a fresh id (Rack's delete_session contract).
assert_nil store.delete_session({}, sid, drop: true)
# Without :drop, a fresh session id is returned.
assert_not_nil store.delete_session({}, sid, {})
end
設計判断
この変更は CacheStore の実装を CookieStore と同等の振る舞いに揃えることを目的とし、既存の公開インターフェースを変更せずに内部ロジックだけを調整しています。generate_sid の呼び出しを条件付きにしただけなので、他のコードパスへの影響はなく、後方互換性が保たれます。
options[:drop] を正しく処理することで、セッションが意図的に破棄された際に不要なクッキーが再生成されることがなくなり、アプリケーション側で期待通りのセッションライフサイクル管理が可能になります。\
\
この設計は、Rack の仕様に忠実であることと、最小限の変更で既存機能を保全することという二つの観点でバランスが取れた判断と言えるでしょう。