[OpenID][SKIP][Rails] open_id_authenticationのバージョンアップによるYahooユーザがログインできなくなる問題まとめ

先日、SUG(SKIP User Group)のバージョンアップを行ないました(ver1.0系からver1.1へ)。
そのバージョンアップにより、YahooのOpenIDを利用しているユーザがログインできなくなるという障害が発生しました。その際の対応方法を書いておきます。

バージョンアップには、RailsOpenID RPのプラグイン open_id_authentication のバージョンアップが含まれていました。
その差分の中に、open_id_authenticationから返ってくる identity_url が正規化されて返ってくるようになる変更がありました。

https://rails.lighthouseapp.com/projects/8995/tickets/51-patch-open_id_authentication-updated-normalization
http://github.com/rails/open_id_authentication/commit/13248aa0dbcd6d4f824833c18f11054a610bb051

この差分は、以下の仕様のための変更のようです。
http://openid.net/specs/openid-authentication-2_0.html#terminology

しかし、YahooのOpenIDは、以下のようになっています。
https://me.yahoo.co.jp/a/xxxxx#ffff
#ffffのように、フラグメントが追加された identifier です。

バージョンアップ以前はフラグメントも含めた全体を identifier としてプラグインのメソッドから返されていたので、そのままDBに登録してユーザを特定していたのですが正規化されると #ffff が取り除かれるので、バージョンアップ後は、フラグメントがない identifier となり検索してもユーザが特定できず正しくログインできないということが障害の原因のようでした。

対応としては、DBに登録済みのフラグメント付きの identifer を再度正規化して登録しなおすということを実施しました。

この障害対応時に、Rails TokyoのIRCで相談させてもらって非常にたすかりました。
ご意見いただいたみなさん、ありがとうございました。

以下に、その相談のログを載せておきます。

14:05 mat_aki: こんにちは、open_id_authenticationプラグインについて質問させてください。
14:08 mat_aki: YahooのOpenIDには、フラグメントが含まれていますが、最近のバージョンのopen_id_authenticationではフラグメントが正規化で削除されるようで
14:09 mat_aki: https://me.yahoo.co.jp/a/XXXX#XXXXhttps://me.yahoo.co.jp/a/XXXX となってしまいます。
14:10 mat_aki: 以前のバージョンでは、フラグメントの削除を行なっていなかったので、URLが一致せず同じユーザと認識されなくなってしまいました。
14:10 mat_aki: これを対応された方っていらっしゃいますか?
14:12 mat_aki: 既に登録されているものからフラグメント部分を削除すればよいような気もしているのですが、それでよいのか悩んでいます。
14:28 littlestarling:      # strip any fragments
14:29 littlestarling:      identifier.gsub!(/\#(.*)$/, '')
14:29 littlestarling: 見た感じここだけっぽいですけどどうなんでしょうね
14:43 mat_aki: そうなんです。そこが2008/12くらいに追加されていて、以前と動作が変わってしまっているのです。
14:54 littlestarling: If the URL contains a fragment part, it MUST be stripped off together with the fragment delimiter character "#".
14:54 littlestarling: OpenID Authentication2.0の方にこう書かれてる以上、Yahooが対応すべきって方向なのかもしれませんが
15:18 mat_aki: http://openid.net/specs/openid-authentication-2_0.html#normalization
15:18 mat_aki: http://openid.net/specs/openid-authentication-2_0.html#identifier_recycling
15:19 mat_aki: この2つの仕様がどうも被っているような雰囲気もしていて、どちらに倒すべきかと。。。
15:26 littlestarling: デリミタに#使うなってことですよね。7.2の方
15:37 mat_aki: 11.5.1 はIDが多くなったら、fragmentsでIDの使いまわしをしてもよいよという仕様かと思っているのですが
15:38 mat_aki: fragmentsとは # のことだとすると、逆のことを言っていて、yahooは11.5.1を利用しているようなのです。
15:39 mat_aki: http://developer.yahoo.co.jp/other/openid/faq.html
15:45 conceal_rs: fragmentは同一IDの再利用時に,前のユーザとは異なることを示すためにつけるユニークな値ですね
15:49 littlestarling: ごめんなさい、URI  = scheme ":" hier-part [ "?" query ] [ "#" fragment ]だから確かに#以下除去したらだめっぽいですね
15:50 conceal_rs: http://www.sakimura.org/modules/wordpress/index.php?cat=15
15:50 conceal_rs: や
15:51 conceal_rs: http://groups.google.co.jp/group/openid-ja/browse_thread/thread/1197c4e283ebbeba
15:51 conceal_rs: にあるんですが,
15:51 conceal_rs: openid.identifier to
15:52 conceal_rs: と openid.claimed_id が違うって言うことじゃないでしょうか.
15:52 conceal_rs: https://me.yahoo.co.jp/a/XXX がユーザ提供識別子で,
15:53 conceal_rs: https://me.yahoo.co.jp/a/XXX#XXX がClaimed Identifierとなり,
15:54 conceal_rs: 認証につかうのは,Claimed Identifier の方ということで,
15:54 conceal_rs: 取ったらダメっぽいですね < #
15:55 conceal_rs: 「# なしで呼び出すと -> # 付きで帰ってくる」ような感じなんでしょうか.
15:55 noplans: 逆じゃないのか……
15:55 noplans: http://openid.net/specs/openid-authentication-2_0.html#terminology
15:56 noplans: Claimed Indentifier は User-Supplied Identifier を normalize したものにみえる
15:57 conceal_rs: では,自分が送る方が fragment 付きってことですかね
15:58 conceal_rs: 正規化でフラグメント取れるんだから,逆でしたねorz
15:58 noplans: さきほどの conceal_rs さんの示した http://www.sakimura.org/modules/wordpress/index.php?p=631 が
15:59 noplans: 合ってる感じします
16:00 conceal_rs: となると,open_id_authentication では,昔はリクエストのURLを保存していたけど,今はレスポンスのURL(正規化済み)を保存するようになったので,合わなくなったってことですかね
16:08 mat_aki: ruby-openidのgemには、climed_idで認証しろっと書いてありますね。
16:08 mat_aki: なるほど。
16:10 mat_aki: しかし、open_id_authenticationから返してもらえるのは normalizeされたdisplay_identifierのみなので、
16:10 mat_aki: open_id_authenticationのバグということでしょうか?
16:12 conceal_rs: データベースにはフラグメント付きで入ってて,最新の open_id_authentication にすると,フラグメント無くなった結果が帰ってくるってことですよね,現状は
16:13 mat_aki: そうです。
16:14 mat_aki: あれ、わからなくなってきました。。。
16:15 conceal_rs: となると,データベースは OpenID 1.1 では正しかったデータで,いまの open_id_authentication は OpenID 2.0 で正しい結果なので,OpenID のバージョンアップによって生じた不整合と言う感じでしょうか
16:16 mat_aki: ほー!!!理解しました。理解力がなくてすいません。
16:19 mat_aki: 対応としては、データベース内からフラグメントを削除する(Open ID 2.0対応で正規化する)ということをすればよいということですね。
16:21 conceal_rs: open_id_authenticationの最新版を使うのなら,それに合わせないと動作しないので,そうなりますね.念のためにバックアップはおすすめします :)
16:21 mat_aki: 了解しました。
16:23 mat_aki: ご助言いただきました皆様ありがとうございました。
16:23 mat_aki: なんとか、理解し解決しました。
16:25 conceal_rs: いえいえー.できればまとめ記事などを書いていただけると助かります ;)
16:28 mat_aki: がんばります。本当にありがとうございました。

最後に、実際にSKIPに流したメンテナンススクリプトは、こちらです。
http://gist.github.com/108678