Codexがブラウザで /reserve?type=event を開いた。
LINEにログインした。認証が完了してページに戻ってきた。
表示されたのはイベント申込み画面ではなく、通常のサービス一覧だった。
最初の修正は不十分だった
window.addEventListener('DOMContentLoaded', ...) の中で、await initLiff() を呼んだ後に location.search からパラメータを読んでいた。
await initLiff();
const p = new URLSearchParams(location.search);
let typeParam = p.get('type');
問題はここだった。liff.init() を呼ぶと、LIFFがURL処理を行う。処理の中で history.replaceState が実行され、location.search の内容が書き換わる。type=event が消える。
だから initLiff() の呼び出しより前に読めばいいと考えた。
const _p0 = new URLSearchParams(location.search);
let typeParam = _p0.get('type');
// liff.stateからの復元処理...
await initLiff();
if (typeParam === 'event') { ... }
Codexが再検証した。まだ通常サービス一覧が表示された。
ページ遷移のことを忘れていた
LIFFの認証フローを整理した。
LINEの外のブラウザで /reserve?type=event を開く。liff.init() が起動する。未ログインと判定される。liff.login({ redirectUri: location.href }) が呼ばれる。
この瞬間、ページがLINEのログイン画面に移動する。
JavaScriptの実行コンテキストがリセットされる。変数はすべて消える。
LINEでログインが完了すると、ブラウザはredirectUriに指定したURLに戻ってくる。新しいページロードが始まる。
ここで typeParam を変数に保持していても意味がない。ページ遷移で消えている。
では location.href の値は何か。liff.login() を呼ぶとき、内部で liff.init() がすでに location.href を書き換えていた可能性がある。もし location.href が https://yoyaku.example.jp/reserve(typeなし)になっていたとすると、redirectUriは typeなしのURLになる。ログイン後に戻るURLも typeなしになる。
2つの対策を重ねた
ひとつ目は redirectUri を明示的に渡すことだ。liff.init() が実行される前の location.href を保存しておき、それを liff.login() に渡す。
const _savedHref = location.href; // liff.init()よりも前に保存する
async function initLiff(redirectUri) {
await liff.init({ liffId: LIFF_ID });
if (liff.isLoggedIn()) {
const profile = await liff.getProfile();
state.lineUserId = profile.userId;
} else {
if (!liff.isInClient()) {
liff.login({ redirectUri: redirectUri || location.href });
}
}
}
initLiff(_savedHref) として呼ぶ。liff.login() には書き換え前の元のURLが渡る。ログイン後に戻るURLには type=event が入っている。
ふたつ目は sessionStorage を使うことだ。ページ遷移をまたいでも sessionStorage は消えない。typeParam を検出したタイミングで保存しておき、ページ再ロード後に読み出す。
if (typeParam) {
sessionStorage.setItem('ttt_type', typeParam);
} else {
typeParam = sessionStorage.getItem('ttt_type') || '';
}
await initLiff(_savedHref);
sessionStorage.removeItem('ttt_type'); // 使い終わったら消す
第1の保護(redirectUri)が機能すれば、ページ再ロード後のURLに type=event が入っている。第2の保護(sessionStorage)は、なんらかの理由でURLからtypeが取れなかった場合のフォールバックだ。
LIFFのURLへの介入は深い
liff.init() がどこまでURLを変えるかは、LIFF SDKの実装に依存する。
通常のブラウザでは history.replaceState を使ってURLを静かに書き換えることができる。ページをリロードせずにURLを変える。これをLIFFがやっている。
LINEアプリの内部ブラウザではこの挙動が違う可能性がある。外部ブラウザでの動作とLINEアプリ内の動作が一致しないことがある。テスト環境をLINEアプリに統一できないと、再現させることも難しい。
今回は「書き換え前に保存して渡す」と「sessionStorageで保険をかける」という2段構えで解決した。どちらか一方が失敗しても、もう一方がカバーする。
Codexの最終検証で「イベントが0件の場合に『現在募集中のイベントはありません』と表示される」ことが確認された。イベント申込み画面へのルーティングが正しく機能していた。
4機能の追加と、その過程で出てきたバグの修正。@32から@36まで、GASのデプロイ番号が4つ進んだ。
*シンプルシステム株式会社 代表 伊藤勝彦*