事例

24時間で無言ログアウト。SPAのJWT失効を根治した設計

SPAが24時間で無反応になる問題の原因と、通信の入口を一本化してJWT失効を検知し自動延長する解決策を事例で解説します。

Papapapapa
JWT認証設計SPAセッション管理フロントエンド
24時間で無言ログアウト。SPAのJWT失効を根治した設計

24時間、無言でログアウト。

画面は開いている。ボタンも押せる。なのにデータだけが更新されない——そんな不具合に心当たりはないでしょうか。今回はあるSPA(シングルページアプリケーション)で実際に起きていた「静かな認証切れ」の話です。

画面は生きているのに、データだけ死ぬ

相談を受けたシステムでは、ログインから24時間が経過するとJWT(認証トークン)が失効する仕様になっていました。問題は、失効した瞬間に何が起きるかです。画面はそのまま表示され続け、ボタンも反応します。ところが裏側のAPI通信だけが静かに失敗し、一覧は更新されず、保存も反映されない。エラーメッセージすら出ないまま、ユーザーは「あれ、おかしいな」と首をかしげてページを再読み込みするまで、異常に気づけませんでした。

業務時間中にログインしっぱなしで作業する担当者ほど、この症状に遭遇しやすいという皮肉な構造もありました。長時間使う人ほど不便を強いられる設計は、放置していい話ではありません。

エラーを各画面に任せた代償

なぜ「失効しました、再ログインしてください」という単純な案内すら出せなかったのか。原因はAPI呼び出しのたびに、各画面・各コンポーネントが個別にエラー処理を書いていたことにあります。

あるページは通信失敗を握りつぶして何もしない。別のページは汎用エラーとして扱い、原因不明のトースト通知を出す。認証切れという特定の状態を判定するロジックがどこにも一元化されておらず、「今、このユーザーはログアウトすべき状態にあるか」を誰も判断できない構造になっていたのです。

エラー処理をAPIごとに書くこと自体は珍しくありません。しかし認証状態のように、アプリ全体で一貫していなければならない情報を各所に分散させると、こうした検知漏れが必ず発生します。個別最適の積み重ねが、全体としての欠陥を生んでいた典型例でした。

操作中でも切れる期限という前提の欠陥

もう一つの根本原因は、有効期限が「ログインしてから24時間」という固定値だった点です。これは一見公平に見えて、実態とは噛み合っていません。

ログイン直後に離席した人は、24時間のうちほとんどを使わずに終えます。一方、ログインしてからずっと作業を続けている人ほど、期限切れの瞬間に何かしらの操作をしている確率が高くなります。つまり、最もアクティブに使っているユーザーほど、作業の途中で不意打ちのように締め出されるという逆転現象が起きていました。

本来、有効期限は「放置された時間」を測るためのものであり、「使い続けている時間」を罰するためのものではありません。この前提のズレが、静かな認証切れという症状の土台になっていました。

通信の入口を一本化して検知する

解決の方向性はシンプルです。各APIが個別に判断するのをやめ、すべての通信が必ず通る「入口」に、失効検知の処理を1か所だけ置くことにしました。

具体的には、通信ライブラリの共通インターセプタ(すべてのリクエスト・レスポンスを横断的に処理できる仕組み)に手を入れ、認証エラーを検知したらそこで一元的に再ログイン画面への誘導を行うようにしました。個々の画面は「データが取れなかったらどうするか」を考える必要がなくなり、認証に関する判断はすべて入口に集約されます。これにより、どのページからアクセスしても、失効時の挙動が統一されるようになりました。

使い続ければ切れない、を成立させる

もう一段の改善として、有効期限そのものの延長ロジックも組み込みました。考え方は「使っている間は切れない」というスライディングセッションです。ユーザーが何らかの操作をするたびに、裏側で有効期限を延長し、放置された時間だけがカウントされるようにしました。

これにより、アクティブに使い続けているユーザーが作業の途中で締め出される矛盾は解消され、実際に何もせず離席した場合にだけ、適切なタイミングでログアウトが発生するようになります。

この修正一式は、通信の入口となるインターセプタの実装と、有効期限延長の配線を合わせて4ファイル・約90行の追加で実現しています。大掛かりな認証基盤の刷新ではなく、検知と延長という2つの仕組みを正しい場所に配置し直しただけで、症状は根治しました。設計の置き場所を誤らなければ、変更量は驚くほど小さくて済むという好例です。

自社のSPAで確認すべき3点

同じ症状に心当たりがある方は、次の3点を点検してみてください。

  1. 認証エラーが1か所に集約されているか——各画面がバラバラに通信エラーを処理していないか、通信の入口で一元的に判定できているかを確認します。
  2. 操作に応じて有効期限が延びる仕組みがあるか——固定の期限だけに頼っていないか、アクティブなユーザーが不意に締め出されない設計になっているかを見直します。
  3. 失効時にユーザーへ明確な案内が出るか——エラーが握りつぶされて「無言で機能が止まる」状態になっていないか、再ログインへの導線が用意されているかを確かめます。

この3点さえ押さえれば、「画面は動いているのにデータだけ死ぬ」という静かな不具合は、多くの場合未然に防げます。

この記事をシェア

関連記事