ちょっと前に話題になったOpenID Connectの仕様とは?

OpenID Connectでググってみたものの、紹介記事ばかりで中身を説明してる人があんまりいないようでした。
仕様ドキュメント?かどうかわかりませんが、まともなドキュメントは http://openidconnect.com/ しかありません。
英語読めるエンジニアならこれ読んでだいたいわかってるのかもしれませんが、ざっとまとめておきます。

3行で説明

  • OAuth 2.0を使って、URL形式のUser Identifierを返す
  • User Identifierは User Info APIとして属性情報取得に利用できる
  • host-metaを利用した独自Discoveryと、DynamicにClient登録のしくみがある

処理フロー

OpenID Connect on Web Server Profile

シーケンスはこんな感じですね。

ここでは、一番処理の多いケースを考えます。
条件はこんな感じです。

  • Client(RP)はユーザーがどのAuthZ Server(OP)のユーザーを使っているかわからない
  • AuthZ Serverのホワイトリストは行わない
  • ユーザー入力から処理が始まる

1. Input URL/email

ユーザーはドメイン/URL/emailなどを入力します。
Clientはこれを定義済のルールに沿って正規化します。

https://recordond.myopenid.com -> https://recordond.myopenid.com/
facebook.com -> http://facebook.com/
recordond@gmail.com -> acct:recordond@gmail.com

http://openidconnect.com/

URLの場合はhttp://を補完する、emailの場合はacct:をつける処理などをします。
とりあえず、ここでユーザーがリダイレクトされたいAuthZ Serverにあたりがつきます。

2. Discovery

ClientはAuthZ ServerのAuthZ Endpointを知りません。それどころか、ClientIDもSecretありません。
ClientID/Secretを取得するには、AuthZ ServerにDynamicなClient Registration Requestを送る必要があります。
ClientはAuthZ ServerのToken Endpointを探すために、host-metaファイルを見に行きます。
このとき、JRD(XRDをJsonで表現したようなやつ)により、Token Endpointを知ることができる。

3. Dynamic Association

Clientは、redirect_uriを送信してClientID/Secret等を取得します。

GET /oauth/token?type=client_associate&
redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2callback HTTP/1.1
Host: server.example.com

http://openidconnect.com/

きっとこのtypeはgrant_typeに相当します。
返される値はこんな感じです。

  • client_id
  • client_secret
  • expires_in
  • flows_supported : AuthZ ServerがWebServer/User-Agent Profileの両方をサポートしているかなど
  • user_endpoint_url : ここにTokenEndpointやUser Auth Endpointなどが含まれると思います。

ようやく、OAuthの処理に入ります。

4. Redirect to User Endpoint

Clientは、AuthZ Serverにユーザーを送ります。
このとき、OAuth 2.0で定義されている"scope"パラメータをOpenID専用のもの("openid")にします。
emailを欲しがるときはscopeにemailも含むらしいとか、微妙な仕様だと思うところもあります。

GET /authorize?type=web_server&scope=openid&
client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com

http://openidconnect.com/

これで、AuthZ ServerはOpenID Connectのフローであることがわかります。
immediateモードのときは、"user_id"パラメータとして、Clientにログイン状態のユーザー識別子をつけます。

5. AuthN/AuthZ

省略

6. Redirect to Client

省略(ここは普通のOAuth 2.0の処理です)

7. Obtain Access Token and User Identifier

ここで、Clientは6で受け取った"code"パラメータを用いてAccess Tokenを取得します。
このとき、OpenID Connectのフローであるときは以下のパラメータを含みます。

* user_id - A unique HTTPS URI of the currently signed in user. e.g. "https://example-server.com/08lnwon1n21" or "https://graph.facebook.com/24400320"
* issued_at - A unix timestamp of when this signature was created.
* signature - HMAC-SHA256 with the key being the client Secret and the text being the access token, expires in, issued at, and user identifier.

http://openidconnect.com/

Clientは、Client Secretを用いてSignatureの検証をします。

8. Obtain User Information
検証が問題なかったら、受け取ったuser_idにAccess Tokenをつけてアクセスします。
すると、こんな結果が返ってきます。

The response is a JSON object which contains some (or all) of the following reserved keys:
* user_id - e.g. "https://graph.facebook.com/24400320"
* asserted_user - true if the access token presented was issued by this user, false if it is for a different user
* profile_urls - an array of URLs that belong to the user
* display_name - e.g. "David Recordon"
* given_name - e.g. "David"
* family_name - e.g. "Recordon"
* email - e.g. "recordond@gmail.com"
* picture - e.g. "http://graph.facebook.com/davidrecordon/picture"

http://openidconnect.com/

Sregみたいですね。この仕様はもう少しよく考えた方がいいと思います。
最初からPoCoにそろえるとか。

OpenID Connect on User-Agent Profile

Web Server Profileだけと書いていなかったので、User-Agent Profileに乗せた場合はこうなりそうです。

まとめ

とりあえず、OpenID Connectの仕様と思われる部分を抜き出しました。
Discoveryの部分やScope周り、あとUserInfoAPIの実装についてはもう少しよく考える方がよさそうです。