JavaScriptでうっかり数値に変換していて、えらいことになった。
ユーザーID
Twitterでログインするサービスを使っているとき、(少なくとも内部的には)@kusano_k のようなIDをIDに使うことはできない。このIDは変更可能であり、他のユーザーが取得する可能性があるから。Twitterユーザーには、このIDとは別に変更不可能な整数のIDが割り当てられている。Twitter APIではユーザーが目にするほうはscreen_name
、目にしない整数のほうはid
と呼ばれている。
2016年2月に登録されたアカウントから、このIDの桁数が一気に増えている。私のサービスに登録してくれている人の中だと、49????????の33 bitから69????????????????の60 bitに増えている。2021年2月に登録したアカウントだと、13?????????????????の61 bit。
これをJavaScriptで数値として扱ってしまうと、値が変わってしまう。JavaScriptのNumber
は倍精度浮動小数点数であり、整数を正確に表現できるのは53 bitまで(最近のJavaScriptはBitInt
という多倍長整数もあるけど)。
> var x = 1234567890123456789;
> console.log(x);
1234567890123456800
ツイートのIDで以前に話題になっていたが、ユーザーIDも気をつけなければいけなかった。
ログイン時にIDを取得して、(うっかり値が変わり)それをそのまま使っているのでだいたい動いていて気が付かなかった。保存したIDを使ってバッチでプロフィール画像を取得するところが失敗していたけれど、削除済みアカウントの失敗に紛れていた。削除されていないアカウントも失敗しているので、てっきり変なフォーマットの画像でも使われているのかと思ったら……。
数値のid
とは別に文字列のid_str
があるので、JavaScriptではここから読み、IDは文字列として扱いましょう。昔からTwitterを使っているとビット数の小さいIDのままなので、自分のメインのアカウントだけではなく、新しく作ったアカウントでもテストをしたほうが良いかもしれない。
対処法
だいたい動いているのだから、丸められたIDを使い続けることも一瞬考えたけれど、それはない。丸めて同じIDになる人がいたら大変なことになる。
screen_name
を保存していれば、そこからIDを引くこともできる。ただ、screen_name
を変えているユーザーがいると困る。
周囲の有効なIDを探すことにした。例えば、丸められて1234567890123456800になるIDは、1234567890123456641(1234567890123456800-159)から、1234567890123456895(1234567890123456800+95)の255個。
> 1234567890123456640
1234567890123456500
> 1234567890123456641
1234567890123456800
> 1234567890123456895
1234567890123456800
> 1234567890123456896
1234567890123457000
正負の幅が同じでないのとか、256個じゃないの?とか気になるが……。まあ、61-53=8 bit分の±256くらい試せば良い……んじゃないのかな。
users/lookup
を使えば、1回のAPI呼び出しで最大100個のIDをチェックできる。
周囲を探して見つからなければ削除されたアカウント、1個見つかればそれが正しいID、2個以上見つかったらそのアカウントに2人がログインできたということでとてもマズい。