Googleログインでゲストデータを引き継ぐ設計|初回登録と既存会員を分けた理由
Appli Parkでは、初回のGoogle登録だけ現在のゲストIDを会員へ昇格させ、登録済み会員への再ログインではゲストデータを自動統合しません。その理由と実装の分岐、確認したケースを紹介します。
CONTENTSこの記事の目次9項目
Appli Parkでは、会員登録をしなくてもshare&do、coffee diary、習慣マネージャーなどのアプリケーションを使い始められます。 ところが、別の端末でも続きを使いたい場面では、端末のCookieだけに頼れません。 そこでGoogleログインを追加するとき、「初回登録」と「登録済み会員の再ログイン」を同じ処理にせず、ゲストデータの扱いを分けました。
登録なしですぐ使えて、必要になったらログインできるようにしたかった
最初から登録を必須にすると、少し試したいだけの人にもGoogleログインを求めることになります。Appli Parkでは、初回アクセス時にサイト共通の内部ユーザーIDを発行し、ブラウザにはそのIDへたどり着くためのゲストCookieだけを持たせています。各アプリの記録はこの内部ユーザーIDに結び付きます。ログイン後も同じIDを使えれば、アプリごとのデータをコピーしたり、所有者IDを書き換えたりせずに引き継げます。
ゲスト利用・初回登録・再ログインの3つの場面
| 利用場面 | データと内部ユーザーIDの関係 | ログイン時の扱い |
|---|---|---|
| 未ログインのゲスト | サーバー上の内部ユーザーIDと、ブラウザのゲストCookie | 同じブラウザから初回登録した場合だけ、その内部IDを会員へ昇格 |
| 初めてGoogle登録する人 | ゲストの内部ユーザーID、または新しく作る会員ID | 有効なゲスト資格情報があれば同じIDを利用。なければ新しい会員を作成 |
| 登録済み会員のログイン | Google認証に紐付いた既存の内部ユーザーID | 現在のブラウザに別のゲストデータがあっても自動統合しない |
初回登録時だけ、ゲストの内部ユーザーIDを引き継ぐ
Google認証から戻ると、バックエンドはまず、認証結果の発行元と利用者を表すsubの組み合わせが、すでにAppli Parkの会員へ登録されているかを調べます。 まだ登録されていないGoogleアカウントで、現在のブラウザに有効なゲストCookieがあれば、そのゲストの内部ユーザーを新しい会員へ昇格させます。 各アプリケーションのデータは同じ内部ユーザーIDを参照しているため、個別にコピーする必要がありません。 昇格後は元のゲスト資格情報を失効させ、新しい会員セッションを発行します。
val existingIdentity = findByIssuerAndSubject(issuer, subject)
val guestIdentity =
if (existingIdentity == null) resolveGuestCookie() else null
val user = existingIdentity?.user
?: guestIdentity?.user
?: createMember()
if (existingIdentity == null) {
linkExternalIdentity(user, issuer, subject)
}
if (guestIdentity != null) revokeGuestCookie()既存会員へゲストデータを自動統合しない理由
- 意図しない上書き:別端末で作った新しいゲスト記録が、会員側の既存記録へ勝手に混ざる可能性がある
- 別人のデータ混入:共有端末では、直前に使ったゲストとGoogleアカウントの本人が同じとは限らない
- 重複データ:同じ内容をゲスト側と会員側の両方で作っていた場合、自動判定だけでは正しくまとめられない
このため、Googleアカウントがすでに登録済みなら、現在のブラウザにゲストCookieがあってもゲスト資格情報を解決しません。 既存会員の内部ユーザーIDで新しいセッションを作り、ゲスト側はそのまま残します。 便利さだけを優先して自動統合すると、誰のデータかという境界を壊しかねないためです。 後付けで統合するなら、対象データを利用者に見せ、アプリごとの重複ルールと確認操作を用意する必要があります。
氏名・メールアドレス・プロフィール画像を保存しない判断
ログインの目的は、GoogleのプロフィールをAppli Park内に表示することではなく、同じ本人が別端末から同じ内部ユーザーIDへ戻れるようにすることです。 バックエンドが保存する外部認証情報は、Cognitoの発行元とsubの組み合わせです。 氏名、メールアドレス、プロフィール画像、Googleのパスワードは、Appli ParkのDB・画面・アプリログへ保存しません。 ブラウザにもGoogleやCognitoのトークンを置かず、バックエンドが認証コードを交換したあと、Appli Park独自のHttpOnlyセッションCookieを発行します。
ログイン時に動きを分けた6つのケース
| ケース | 期待する動き | 実装上の扱い |
|---|---|---|
| ゲストデータあり+初回登録 | ゲストの記録をそのまま使える | ゲストの内部ユーザーをMEMBERへ変更し、外部認証IDを追加。ゲスト資格情報は失効 |
| ゲストデータなし+初回登録 | 空の新規会員として始まる | 新しい内部ユーザーを作成し、Cognitoのissuer・subを対応付ける |
| 登録済み会員+別のゲストCookieあり | 既存会員のデータへ戻る | 既存の外部認証IDを優先し、現在のゲストは解決も失効もさせない |
| 登録済み会員+別端末 | 同じ会員データを利用できる | Google認証から既存の内部ユーザーを特定し、その端末用の会員セッションを発行 |
| Cookie削除後に初回登録 | 削除前の未登録ゲストは復元しない | 有効なゲスト資格情報がないため、新しい会員を作成 |
| 引き継ぎ処理の重複実行 | 同じゲストを二重移行しない | 1回目で外部認証IDが登録されるため、次回以降は既存会員の分岐へ進む |
今回実装しなかったこと
- 登録済み会員へのゲストデータの後付け統合
- Googleの氏名・メールアドレス・プロフィール画像の取得・表示
- ゲストと会員の記録を画面上で選んで統合する機能
引き継ぎ対象の3アプリと、端末内に残すワインQ
share&do、coffee diary、習慣マネージャーは、ゲストでも会員でも同じサイト共通の内部ユーザーIDへデータを保存します。そのため初回登録時にIDを維持すれば、3アプリを個別に移行する処理は要りません。一方、ワインQの「あとで見返す」問題IDはlocalStorageへ保存しており、Googleログインとは結び付けていません。すべてのデータを一律に同期するのではなく、用途と保存場所を分けています。 ※今後作成するアプリケーションのデータについては、適宜判断していきます!
READ MOREshare&doを使う共有リストの作成・参加・TODO更新を、ゲストのまま始められます。→READ MOREcoffee diaryを使う豆や淹れ方、苦味・酸味の記録を同じ内部ユーザーIDへ保存します。→READ MORE習慣マネージャーを使う登録した習慣と日ごとの達成状況を、ログイン後も続けられます。→READ MOREワインQを使う「あとで見返す」問題は端末内保存のため、今回は会員同期の対象外です。→引き継ぎは、コピーより「同じ利用者として続ける」
今回の設計で大切にしたのは、初回登録ではゲスト時代の内部ユーザーをそのまま会員へ昇格し、登録済み会員では別のゲストを勝手に混ぜないことです。 同じGoogleログインでも、初回と再ログインでは守るべきデータが違います。便利そうだから自動統合するのではなく、どのIDを正とするか、混ぜてよいデータか、失敗したときに戻せるかを先に決める必要がありました。
出典・参考資料OpenID ConnectGoogle for Developers。認証リクエスト、state・nonce、IDトークン検証などの基本フローを確認。developers.google.com