コンテンツにスキップ

セッション切れ再認証

昼休みに切れたセッションが、それを発見したクリックまで食べてしまう べきではありません。このレシピでは、保護されたアクションの 401 が 共有ホストへ retarget されたログイン <dialog> を運びます — installRemoteDialog (出荷済み)がそれを開きます。そして自動インストールされる installSessionExpiry() が中断されたリクエストを覚えていて、ログイン 応答が hc:sessionrenewed を発火した瞬間に再実行します。ユーザーの 操作は消えずに完了します。 バージョニングポリシーの 対象です。

別名: セッションタイムアウト、再ログイン。

デモのセッションは切れた状態から始まります: Approve をクリックし、 サインインして(どのパスワードでも可、wrong はインラインエラー)、 承認がひとりでに完了するのを見てください。Expire session で フローを再武装できます。

The session starts expired — click Approve.

<button class="hc-button" data-variant="primary" type="button"
data-hx-post="/tickets/7/approve"
data-hx-target="#status" data-hx-swap="innerHTML">
Approve
</button>
<p class="hc-field__hint" id="status" aria-live="polite"></p>
<!-- body 末尾の共有ホスト 1 つ -->
<div id="error-dialog" data-hc-remote-dialog-root data-hc-session-expiry></div>

ホストは 2 つの帽子をかぶります: data-hc-remote-dialog-root は サーバーがスワップしてきたダイアログを開き、data-hc-session-expiry はリプレイブリッジを武装します。

ケース応答
保護されたエンドポイント、セッション切れ、htmx リクエスト401 + HX-Retarget: #error-dialog + HX-Reswap: innerHTML + ログインダイアログのフラグメント。サーバーは処理の前に 401 しなければなりません — リプレイの安全性はそこに依存します
ログイン成功200 + 空ボディ + HX-Trigger: {"hc:sessionrenewed": {}} — ブリッジがダイアログを閉じてリプレイします
ログイン失敗422 + HX-Retarget: #error-dialog + HX-Reswap: innerHTML + field-errors をインラインに再描画したダイアログ — ログインフォームは data-hx-target="this" / data-hx-swap="none" で POST するため、ヘッダーがなければボディは飲み込まれます
非 htmx リクエストでのセッション切れ?next= 付きログインページへの 303

スロットは 1 つ、最新が勝ちます: 並行して中断された場合は最後の 1 件だけがリプレイされます。リプレイは htmx.ajax(verb, path, { source, values }) を通り、通常の パイプラインを再走します — ローテートされた CSRF トークンは installCsrfHeader が毎リクエスト読み直し、インジケーターも ターゲットも、ユーザーがもう一度クリックしたかのように振る舞います。 複数値フィールドは最後の値に畳まれます。中断された要素が DOM を 離れていればリプレイはスキップされます。ページロードをまたいで 何も生き残りません。

  • ダイアログは showModal() による本物の <dialog> です — ネイティブの フォーカストラップと Escapearia-labelledby が名前を与えます。
  • Cancel は <form method="dialog"> のボタン — 宣言的で CSP 安全です。
  • リプレイの結果はアクション自身の aria-live ターゲットに着地する ため、完了が読み上げられます。