OpenIDでGoogleからグローバルユニークなユーザー識別子を取得できるかもしれない方法

  • GoogleOpenIDってrealm単位で変わるんですよねー。」
  • 「いわゆるPPIDですよね。他のサイトと識別子変えてくれるので名寄せできないんですよ。」
  • ドメイン変わったときにユーザー引き継ぎたいときはどうすれば?Gmail(メアド)でむりやりつなぐしかない?」

この話は前からありましたが、最近Janrain Engage (formerly RPX)を久々に使ったら、GoogleのユニークなIdentifierっぽいのが取れました。
そのOpenID AuthNリクエストからその取得方法を調べました。

※Janrain EngageってのはシンプルなAPIで簡単にOpenIDのRPやtwitter/facebookのOAuth Cnsumer/Clientになれちゃうサービス?プロダクト?です。
Janrain Engageの仕様などについてはここでは触れません。

振り返り;OpenIDのフローでGoogleから渡されるUser Identifier

まずはこの部分の復習です。
まずは、最低限のOpenIDのAuthNリクエストを送ってみます。

【Request】

https://www.google.com/accounts/o8/ud?
&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0
&openid.mode=checkid_setup
&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select
&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select
&openid.realm=http%3A%2F%2Frp.example.com%2F
&openid.return_to=http%3A%2F%2Frp.example.com%2Freturn_to

※ちなみに、rp.example.comってのは存在しません。

同意画面の後に、こんなレスポンスが返ってきます。

【Response】

http://rp.example.com/return_to?
openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0
&openid.mode=id_res
&openid.op_endpoint=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fud
&openid.response_nonce=(nonceの値)
&openid.return_to=http%3A%2F%2Frp.example.com%2Freturn_to
&openid.assoc_handle=(Assoc Handleの値)
&openid.signed=op_endpoint%2Cclaimed_id%2Cidentity%2Creturn_to%2Cresponse_nonce%2Cassoc_handle
&openid.sig=(Signatureの値)
&openid.identity=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid%3Fid%3DAItOawlgrB6aWhb9_h3D_N6aSOsyFbQNsak5ej0
&openid.claimed_id=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid%3Fid%3DAItOawlgrB6aWhb9_h3D_N6aSOsyFbQNsak5ej0

リクエストのopenid.realmパラメータを変更すると、このopenid.claimed_idの/id/以降の文字列が変わります。
私のアカウントを晒すと、こんな感じでした。

これがrealmベースのPPIDってやつですね。

秘密の?AXパラメータによる識別子取得方法

ずばり、こんな感じです。

"http://schemas.openid.net/ax/api/user_id"というAXのパラメータを追加する

それでは実践してみましょう。
正式な実装方法は書いていませんでしたが、いろいと組み合わせた結果、こんな感じでいけそうでした。

【Request】

https://www.google.com/accounts/o8/ud?openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0
&openid.mode=checkid_setup
&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select
&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select
&openid.realm=http%3A%2F%2Frp.example.com%2F
&openid.return_to=http%3A%2F%2Frp.example.com%2Freturn_to
&openid.ns.ax=http%3A%2F%2Fopenid.net%2Fsrv%2Fax%2F1.0
&openid.ax.mode=fetch_request
&openid.ax.required=email%2Cguid
&openid.ax.type.email=http%3A%2F%2Faxschema.org%2Fcontact%2Femail
&openid.ax.type.guid=http%3A%2F%2Fschemas.openid.net%2Fax%2Fapi%2Fuser_id

この行がポイントです。

&openid.ax.type.guid=http%3A%2F%2Fschemas.openid.net%2Fax%2Fapi%2Fuser_id

これでレスポンスを見てみます。

【Response : rp.example.com

http://rp.example.com/return_to?openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0
&openid.mode=id_res
&openid.op_endpoint=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fud
&openid.response_nonce=(nonceの値)
&openid.return_to=http%3A%2F%2Frp.example.com%2Freturn_to
&openid.assoc_handle=(Assoc Handleの値)
&openid.signed=op_endpoint%2Cclaimed_id%2Cidentity%2Creturn_to%2Cresponse_nonce%2Cassoc_handle%2Cns.ext1%2Cext1.mode%2Cext1.type.guid%2Cext1.value.guid%2Cext1.type.email%2Cext1.value.email
&openid.sig=(Signatureの値)
&openid.identity=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid%3Fid%3DAItOawlgrB6aWhb9_h3D_N6aSOsyFbQNsak5ej0
&openid.claimed_id=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid%3Fid%3DAItOawlgrB6aWhb9_h3D_N6aSOsyFbQNsak5ej0
&openid.ns.ext1=http%3A%2F%2Fopenid.net%2Fsrv%2Fax%2F1.0
&openid.ext1.mode=fetch_response
&openid.ext1.type.guid=http%3A%2F%2Fschemas.openid.net%2Fax%2Fapi%2Fuser_id
&openid.ext1.value.guid=(グローバルユニークなユーザー識別子)
&openid.ext1.type.email=http%3A%2F%2Faxschema.org%2Fcontact%2Femail
&openid.ext1.value.email=(メアド)

こんな感じで、(グローバルユニークなユーザー識別子)が返ってきます。
realmとreturn_toのドメインをrp2.example.comに変えてみます。

【Response : rp2.example.com

http://rp.example.com/return_to?openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0
&openid.mode=id_res
&openid.op_endpoint=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fud
&openid.response_nonce=(nonceの値)
&openid.return_to=http%3A%2F%2Frp2.example.com%2Freturn_to
&openid.assoc_handle=(Assoc Handleの値)
&openid.signed=op_endpoint%2Cclaimed_id%2Cidentity%2Creturn_to%2Cresponse_nonce%2Cassoc_handle%2Cns.ext1%2Cext1.mode%2Cext1.type.guid%2Cext1.value.guid%2Cext1.type.email%2Cext1.value.email
&openid.sig=(Signatureの値)
&openid.identity=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid%3Fid%3DAItOawkIYyIzr6aa3CtLAlL4ESBi-2ytKUGcEfY
&openid.claimed_id=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid%3Fid%3DAItOawkIYyIzr6aa3CtLAlL4ESBi-2ytKUGcEfY
&openid.ns.ext1=http%3A%2F%2Fopenid.net%2Fsrv%2Fax%2F1.0
&openid.ext1.mode=fetch_response
&openid.ext1.type.guid=http%3A%2F%2Fschemas.openid.net%2Fax%2Fapi%2Fuser_id
&openid.ext1.value.guid=(グローバルユニークなユーザー識別子)
&openid.ext1.type.email=http%3A%2F%2Faxschema.org%2Fcontact%2Femail
&openid.ext1.value.email=(メアド)

はい、同じ値が返ってきました。あ、いや、ここでは隠してるのでわかんないですが、同じ値が返ってきます。
ちなみに、この文字列を"https://www.google.com/profiles/"の後ろにつけると、プロフィールURLにリダイレクトします。
あ、Yahoo! JAPANOpenIDみたいですねっ!

まとめ

  • AXのRequestに"http://schemas.openid.net/ax/api/user_id"というパラメータを追加するとできそう
  • ちなみに、他のAXパラメータと組み合わせないと値が来ないっぽい(詳細不明)
  • Realmを変えてももちろん返る値は固定
  • "https://www.google.com/profiles/"の後ろにつけると、プロフィールURLにリダイレクトするURLになる

これで複数RP間の名寄せ可能です。
今後、このパラメータがどうやって使われるのかはもちろん私にはわかりませんが、GoogleOpenIDとつなぐサービスを構築する機会があるかたはこの仕様を覚えておいてもらえれば役に立つかもしれません。

試してみてください。
ではまた!