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/
http://openidconnect.com/
facebook.com -> http://facebook.com/
recordond@gmail.com -> acct:recordond@gmail.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&
http://openidconnect.com/
redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2callback HTTP/1.1
Host: server.example.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&
http://openidconnect.com/
client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.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"
http://openidconnect.com/
* 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.
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:
http://openidconnect.com/
* 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"
Sregみたいですね。この仕様はもう少しよく考えた方がいいと思います。
最初からPoCoにそろえるとか。
OpenID Connect on User-Agent Profile
Web Server Profileだけと書いていなかったので、User-Agent Profileに乗せた場合はこうなりそうです。
まとめ
とりあえず、OpenID Connectの仕様と思われる部分を抜き出しました。
Discoveryの部分やScope周り、あとUserInfoAPIの実装についてはもう少しよく考える方がよさそうです。