ユーザーの内部IDの発行権を他人に握らせてはいけない

結論
ユーザーの内部IDを自システム以外に委ねるべきではありません。
ユーザーの内部IDの実装について気をつけるべきことを2つ紹介します。
-
外部サービスが発行したIDを内部IDにするべきではない
-
ユーザーが変更可能な値を内部IDにするべきではない
外部サービスが発行したIDを内部IDにするべきではない
外部サービスが発行したIDを内部IDにすることは避けましょう。
内部IDは、システム内で一意で永続的な識別子であるべきです。
例) 外部IDプロバイダのユーザーID
例えば、GoogleをIDプロバイダとして利用する上で、GoogleアカウントのユーザーIDをそのままユーザーの内部IDとして使用した場合、どんな問題が起こるかを考えてみましょう。
例えば、後からGoogleがユーザーIDの仕様を変更した場合、システム内でのユーザーIDの一意性が保証されなくなります。もちろん、GoogleがユーザーIDを変更することは稀ですが、もしそうなった場合、システム全体に影響が及ぶ可能性があります。
また、後から他のIDプロバイダを追加した場合、ユーザーIDの一意性を保つために、複雑なロジックが必要になるかもしれません。
// 複雑になってしまった例
type UserId = {
type: 'Google' | 'Facebook' | 'Twitter';
value: string;
}
SELECT * FROM companies c
LEFT JOIN company_members cm ON cm.company_id = c.company_id
LEFT JOIN users u ON u.id = cm.id AND u.id_type = cm.id_type;
-- id_typeを指定し忘れた場合、誤ったユーザーIDを結合して障害になりそう
例) 外部サービスのIDを内部IDにする場合
先ほどの例は、外部IDプロバイダのユーザーIDをそのまま内部IDに利用してしまう、という非常に特異なケースでした。
しかし、AWS CognitoユーザープールやAuth0などのIDaaSを利用する場合、外部サービスのIDを内部IDとして利用することはよくあります。
そして、そのような場合も、外部サービスのIDをそのまま内部IDとして利用することは避けるべきです。
例えば、AWS CognitoユーザープールのユーザーID (sub) をそのまま内部IDとして利用する場合、以下のような問題が発生する可能性があります。
例) ユーザー名の変更時にIDが変更されてしまう場合
AWS Cognitoユーザープールでは、ユーザー名を変更できません。(別途変更可能な preferred_username 属性を利用することはできます。)
この問題を解決するために、「ユーザー名を変更する場合、内部的にはCognitoユーザーを削除して再登録する」という実装をすることが考えられます。
しかし、CognitoユーザーのID(sub)をそのまま内部IDとして利用している場合、ユーザーを削除して再登録することで、内部IDが変わってしまいます。
このCognitoユーザープールに単一のシステムが依存している場合はまだしも、複数のシステムが依存しているとしたらどうでしょう。どうやって安全に新しいID体系へ移行するのでしょうか?
このような場合、あらかじめ内部IDを別途採番する実装をすることが望ましいです。
ユーザーが変更可能な値を内部IDにするべきではない
ユーザーが変更可能な値をシステムのためのID(内部ID)にすることは避けましょう。
IDは一意で永続的な識別子であるべきであり、ユーザーが変更できる値はその特性に反します。
例) ユーザーの内部ID
ユーザーの内部IDを、ユーザーが変更可能な値にした場合、どんな問題が起こるかを考えてみましょう。
例えば、ユーザーの内部IDをユーザー名にする場合、ユーザーがユーザー名を変更すると内部IDも変わってしまいます。
これにより、以下のような問題が発生します。
- リレーションシップの破壊
他のデータベースのテーブルや外部システムとのリレーションシップが、ユーザー名の変更によって壊れてしまいます。
- 監査ログ
ユーザーの行動を追跡するためのログが、ユーザー名の変更によって無効になります。
ユーザーがユーザー名を変更しなければ良いのか?
ユーザーがユーザー名を変更不可とする実装は現実的ではありませんが、「初期リリースでは変更不可」などの意思決定が取られることはあります。
では、その場合には問題は発生しないのでしょうか?
以下の例を見てみましょう。
-
ユーザーAが
fooとして登録 -
ユーザーAが退会
-
ユーザーBが
fooとして登録
この場合、他のデータベースのテーブルや外部システムとのリレーションシップがきちんと修正されていないと、ユーザーBがユーザーAのデータにアクセスできてしまう可能性があります。
近年では、個人情報の漏洩は企業の大きな信用毀損リスクとなるため、「内部IDを別途採番する実装コスト」と「個人情報の漏洩リスク」を天秤にかけた場合、後者のリスクが圧倒的に大きいです。
もっと分かりやすく言えば、内部IDの実装をするための(多くても)1週間と、個人情報漏洩の問題を顧客に説明し、再発防止策を講じ、徹底的に調査するための1ヶ月を天秤にかけてみてください。
また、サービスがスケールした場合にデータ分析をする場合、過去の差分データがどのユーザーに紐づいているかを正確に把握するためには、内部IDが必要です。せっかくサービスがPMFを達成しても、得られたデータがゴミになってしまい、次の施策が打てなくなってしまうかもしれません。