セッションの寿命を10年に伸ばしたのに、苦情が数週間止まらなかった
Clerk のセッションが7日で切れていた。設定を直したのに「また勝手にログアウトされた」が届き続けたのは、既に発行済みのセッションには新しい設定が遡って適用されないから。
前回の記事(Apple の「メールを非公開」で、ユーザーが永久にログインできなくなる)と同じ、「勝手にログアウトされる」という報告を追いかけていたときの話。
こちらは原因がはっきりしていて、設定を1つ変えれば直った。にもかかわらず、直したあと数週間、同じ苦情が届き続けた。その理由のほうが学びだった。
「よく開くとログアウトになる」
あるユーザーからこう言われた。
よく開くとログアウトになっています
「よく」という言葉を、私は最初「頻繁に」という感覚的な表現として受け取った。体感の話だろうと。
これは正確な証言だった。感覚ではなく、周期があった。
セッション履歴を時系列で並べる
Clerk の Backend API でそのユーザーのセッション一覧を取った。
$sk = "sk_live_..."
$h = @{ Authorization = "Bearer $sk" }
$s = Invoke-RestMethod -Uri "https://api.clerk.com/v1/sessions?user_id=USER_ID&limit=20" -Headers $h
$epoch = [datetime]::SpecifyKind([datetime]"1970-01-01","Utc")
function T($ms){ if($ms){ $epoch.AddMilliseconds([double]$ms).ToLocalTime().ToString("yyyy/MM/dd HH:mm") } else { "-" } }
$s | Sort-Object created_at -Descending | % {
"{0,-9} | created={1} | expire={2}" -f $_.status,(T $_.created_at),(T $_.expire_at)
}
出てきたのがこれ。
| セッション作成 |
|---|
| 6/17 21:40 |
| 6/24 22:24 |
| 7/01 22:32 |
| 7/08 22:56 |
きれいに7日おき。 そして各セッションの expire_at は、いずれも created_at のちょうど7日後だった。
つまりこのユーザーは、7日ごとに強制的にログアウトさせられ、その都度律儀にサインインし直していた。1か月以上、文句も言わずに。「よく開くとログアウトになっています」は、まったく正確な報告だった。
Clerk のセッション寿命の設定を見に行ったら、デフォルトの7日のままだった。触った記憶が無いということは、最初から一度も触っていなかった。
直した。そして、直らなかった
セッションの最大寿命を10年(3650日)に変更した。
効果はすぐ確認できた。7/16 に新しく作られたセッションの expire_at は 2036年7月13日。created_at + 3650日で計算が合う。設定は正しく効いている。
なのに、その後も「勝手にログアウトされた」という報告が届き続けた。1週間経っても、2週間経っても。
一瞬、設定が効いていないのかと思った。だが新規セッションの expire_at は 2036年になっている。効いている。
答えは、既に発行済みのセッションだった。
セッション寿命の設定は、これから作られるセッションにしか適用されない。設定変更の時点で生きている7日セッションは、そのまま7日で死ぬ。この設定変更は、既に配られたものを書き換えてはくれない。
だから起きたことはこうだった。
- 設定変更の前にサインインした人 → 7日セッションを持ったまま → 期限が来て落ちる → 苦情が来る
- そのユーザーが落ちた後にサインインし直す → そこで初めて10年セッションになる → 以後は落ちない
つまり全員が一度ずつ落ち切るまで、苦情は止まらない。しかも「最後に落ちる人」は、最後にログインした人だ。最大で設定変更から7日、実際にはアプリを開く頻度の分だけ後ろにずれる。
これを理解していなかったせいで、「直したはずなのにまた来た」を何度か疑心暗鬼で見ることになった。修正が効いているかどうかは、苦情の件数ではなく、新しく作られたセッションの expire_at を見て判断すべきだった。
ついでに、自分の思い込みも1つ壊れた
この件を追う前、私はこう考えていた。
落ちているのはメールアドレスで登録した人だろう。Google や Apple でログインしている人は、そもそもコード入力が要らないから無傷のはずだ。
完全に間違っていた。
7日で落ちていたそのユーザーは、password_enabled = false、外部連携は oauth_apple の1件のみ、メールアドレスは @privaterelay.appleid.com。Apple でしか登録していないユーザーだった。
セッションの寿命は、どうやってサインインしたかとは関係がない。認証方式は「落ちた後に戻ってこられるか」を左右するが、「落ちるかどうか」には効かない。この2つを混ぜて考えていた。
(そして、この Apple のみのユーザーが落ちた後に戻ってこられなかった理由が、前回の記事の「メールを非公開」の話につながる。落ちる原因と、戻れない原因は別物だった。)
これで全部ではなかった
正直に書いておくと、セッション寿命を直しても「勝手にログアウトされる」報告は完全には消えなかった。この記事を書いている時点で、少なくとも4つの別々の原因が見つかっている。
- セッション寿命が7日(この記事)— 設定変更で解決
- Apple の「メールを非公開」 — 落ちた後に戻る入口が1つしか無かった(前回の記事)
- 認証ドメインに到達できない片肺状態 — Clerk のセッションは端末に残るので画面上はログイン済みに見えるが、バックエンド用のトークンが発行できず、中身が全部空に見える
- 通信の瞬断で「未サインインのふり」をする — clerk-js はオフライン時に内部でセッションを
nullとして返す実装になっていて、鍵は無事なのに未サインイン扱いに倒れる
同じ症状に見えて、原因は毎回違った。「勝手にログアウトされる」というユーザーの言葉は、症状としては正確でも、原因を1つに絞る手がかりにはならなかった。
学んだこと
設定変更は、既に発行済みのものに遡らない。 セッション、トークン、署名付きURL、キャッシュ。「有効期限を持つものを配る」仕組みは全部これで、直したつもりの変更が実際に行き渡るには、発行済みの分が全部期限切れになるまでの時間がかかる。その待ち時間の分だけ、直したあとも症状が出続ける。
だから「直ったか」を苦情の件数で測ってはいけない。 測るべきは新しく発行された分の中身だった。今回なら新規セッションの expire_at で、それは設定変更の直後に確認できた。苦情が止まるのを待っていたら、2週間ずっと不安なままだった。
ユーザーの言葉は、思っているより正確なことがある。 「よく開くとログアウトになっています」を感覚的な表現だと受け取ったが、実際には7日周期という具体的な事実の報告だった。曖昧に聞こえる証言でも、数字と突き合わせるまで曖昧だと決めつけないほうがいい。