はじめに
昨日はOpenAMをOpenID Providerにしてみました。今日はOpenID Connectのエンドポイントを少したたいてみて、その動きを見ていきたいと思います。
目標はシンプル
わたしがOpenID Connectを使いたい理由って結構シンプルなんです。
- 会社のアプリにシングルサインオンしたい(クロスドメインのアプリを含む)
- そのためにアプリにID Federation(ID連携)したい
- ID連携の情報の中にはOpenAMで誰々さんが認証成功したよっていう情報と**認証した誰々さんの属性情報(プロフィール)**を含みたい
これだけっちゃーこれだけなので、いろんなドキュメントをリファレンスしながら、試してみましょう。
ID Token
いろいろ試す前に、ID Tokenの説明は欠かせません。
ID Tokenとは、認証時のAssertion情報を含んだトークンです。ふわ~っと表現すると、先ほどのOpenAMで認証したよという情報と要求に応じて付加することのできる認証したユーザーの属性情報とかを含みます。
ID Tokenは、JSON Web Tokenという形式で表現されています。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJpc3MiOiJodHRwczovL3NlcnZlci5leGFtcGxlLmNvbSIsInN1YiI6IjI0NDAwMzIwIiwiYXVkIjoiczZCaGRSa3F0MyIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIiwiZXhwIjoxMzExMjgxOTcwLCJpYXQiOjEzMTEyODA5NzAsImF1dGhfdGltZSI6MTMxMTI4MDk2OSwiYWNyIjoidXJuOm1hY2U6aW5jb21tb246aWFwOnNpbHZlciJ9.
kz_LR9UOUnhlCft8CzJHJ58gvu5FI8OoLnAuxt1GFa8
わかりませんね。このトークンは以下のような構成になっています。
BASE64URL (UTF-8 (Header))
.
BASE64URL (UTF-8 (Payload))
.
Base64url (UTF-8 (Signature))
なので、.
でsplitとbase64UrlDecodeするとPayload部の抽出ができます。
こんなかんじ。
{
"iss": "https://server.example.com",
"sub": "24400320",
"aud": "s6BhdRkqt3",
"nonce": "n-0S6_WzA2Mj",
"exp": 1311281970,
"iat": 1311280970,
"auth_time": 1311280969,
"acr": "urn:mace:incommon:iap:silver"
}
各行の意味は以下のような感じ。このPayloadはOpenID ConnectのSpecからぱくってきました(´ρ`)
param | desc | e.g. |
---|---|---|
iss | トークンの発行者で、ようはOpenAM | https://server.example.com |
sub | OpenAM内でのユーザーの識別子 | 24400320 |
aud | どのアプリに向けて発行したか | s6BhdRkqt3 |
nonce | IDトークンくれっ!って要求をIDトークンに関連付けるための情報 | n-0S6_WzA2Mj |
exp | IDトークンの有効期限 | 1311281970 |
iat | IDトークンをつくった時間 | 1311280970 |
auth_time | ユーザーが認証した時間 | 1311280969 |
acr | どんなコンテキストで認証したか? | urn:mace:incommon:iap:silver |
他にもパラメータがあります。詳しくはこのPayloadのパクり元をご参照ください。
Authorization Code Flow
Authorization Code Flow(認可コードフロー)というものを試してみます。
Authorization Code Flowとは、フロント側ではcodeという情報を持ちまわって、ID Tokenをバックチャネルで取得するフローです。OpenID Connectには他にもImplicit Flowがありますが、それと比べて以下のような特性があります。
- User-AgentにID Tokenを公開しなくてもよいので安全
- ID Tokenを発行するときに、ID Tokenを欲しているアプリ(Relying Party)の認証をすることができる
- 上述の認証をするためのシークレット情報を安全に維持できるアプリに適する(Webアプリとか)
Authorization Code Flowは以下のような感じです。
- Relying Partyは認可リクエストをおくる準備をする
- 認可リクエストをOpenID Providerに送信する
- OpenID Providerはユーザー認証する
- OpenID ProviderはRelying PartyにID連携することの同意をユーザーから得る
- OpenID ProviderはAuthorization CodeをRelying Partyに返却する
- Relying PartyはAuthorization CodeをOpenID Providerに送る
- OpenID ProviderはID TokenとAccess TokenをRelying Partyに返却する
- Relying PartyはID Tokenの検証をする
OpenAMにRelying Partyを登録する
ではやっていきましょう!
OpenAMに管理者アカウント(ユーザー名はamadmin
)でサインインします。
Applications => OAuth 2.0 => 新規をクリックします。
以下のパラメータを入力します。
- 名前(client id)
- relying party
- パスワード(client secret)
- password
この設定だけだと何もできないので、先ほど作成したrelyingparty
の設定を開き、以下のパラメータを入力します。
- リダイレクトURI
- スコープ
- openid
- profile
試してみる
本当はちゃんとRelying Partyを用意してやってみたらいいんだけど、今日はお試しです。ブラウザ上で疑似Relying Partyみたいなことをしてみます。
先ほどの1.~8.に沿ってやっていきましょう。
1. Relying Partyは認可リクエストをおくる準備をする
リクエストパラメータをつくります。(パクりもとはこのへん)
param | desc | e.g. |
---|---|---|
response_type | フローを指定 | code |
scope | 要求する情報の範囲を指定 | openid profile |
client_id | Relying Partyの識別子を指定 | relyingparty |
state |
2. と5. を紐づけるコード |
af0ifjsldkj |
redirect_uri | Authorization Codeを返却するコールバックURI | https%3A%2F%2Flocalhost:443%2Fcb |
説明にもあるとおり、stateは本来は後で検証に使うので、Relying Party側で保持しておく必要があります。
2. 認可リクエストをOpenID Providerに送信する
本当はRelying Partyからの302 Found
のLocation Headerで送信されたりしますが、本日はお試しなのでブラウザから送信します。
https://iam.example.com/openam/oauth2/authorize?
response_type=code&
scope=openid%20profile&
client_id=relyingparty&
state=af0ifjsldkj&
redirect_uri=https%3A%2F%2Flocalhost:443%2Fcb
3. OpenID Providerはユーザー認証する
OpenAMのサインイン画面が表示されるので、本連載記事の検証用アカウント(ユーザー名はjohnd
)でサインインします。
4. OpenID ProviderはRelying PartyにID連携することの同意をユーザーから得る
OAuth 2.0でもそうですが、OpenID Connectでは、Client(Relying Party)に対し、ID情報(ID Tokenやプロフィール情報)を連携する同意を求められます。OpenAMでは以下のように、実際に連携するプロフィールが表示されるようになっています。
5. OpenID ProviderはAuthorization CodeをRelying Partyに返却する
連携を許可すると、Relying PartyにAuthorization Codeをコールバックに返却します。
Query Stringに載っているので、コピペします。
https://localhost/cb?
code=95a103d8-c849-41cc-9968-51d4b10c1661&
scope=openid%20profile&
iss=https%3A%2F%2Fiam.example.com%3A443%2Fopenam%2Foauth2&
state=af0ifjsldkj&
client_id=relyingparty
6. Relying PartyはAuthorization CodeをOpenID Providerに送る
先ほどコピペしたAuthorization CodeをPOSTのリクエストに載せて、ID Tokenを要求します。
要求する際にはRelying Partyを認証しますので、Authorization Headerにclient id
とclient secret
を:
で繋げてbase64encodeしたものを載せておきます。
$ curl -k
-H "Content-Type: application/x-www-form-urlencoded"
-H "Authorization: Basic cmVseWluZ3BhcnR5OnBhc3N3b3Jk"
-d "grant_type=authorization_code"
-d "code=95a103d8-c849-41cc-9968-51d4b10c1661"
-d "redirect_uri=https%3A%2F%2Flocalhost:443%2Fcb"
https://iam.example.com/openam/oauth2/access_token
7. OpenID ProviderはID TokenとAccess TokenをRelying Partyに返却する
レスポンスは以下のようなJSONが返ってきます。
ID Tokenがきちんと返却されていますね。
{
"access_token":"601a0158-1aac-45d2-95d5-579068502d5a",
"scope":"openid profile",
"id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdF9oYXNoIjoiZXpLXzBDM3BFSTYxUnkwMmJQMnNBdyIsInN1YiI6ImpvaG5kIiwiYXVkaXRUcmFja2luZ0lkIjoiZDcxNDQ0ODEtNmY3Ny00OTM5LTk0ZDYtYzViZjEzYzZiMmRkLTM1MjEiLCJpc3MiOiJodHRwczovL2lhbS5leGFtcGxlLmNvbTo0NDMvb3BlbmFtL29hdXRoMiIsInRva2VuTmFtZSI6ImlkX3Rva2VuIiwiYXVkIjoicmVseWluZ3BhcnR5IiwiY19oYXNoIjoiXzFBVTZ4ZFI1RUVFTVlwb0JKXzA0USIsIm9yZy5mb3JnZXJvY2sub3BlbmlkY29ubmVjdC5vcHMiOiJhYTVhZWVkZS1hZDk0LTRiNDMtYjBhYi0xZjQ1YjE2N2Q2MTYiLCJhenAiOiJyZWx5aW5ncGFydHkiLCJhdXRoX3RpbWUiOjE0ODEyOTIwOTgsInJlYWxtIjoiLyIsImV4cCI6MTQ4MTI5NzE2NiwidG9rZW5UeXBlIjoiSldUVG9rZW4iLCJpYXQiOjE0ODEyOTM1NjZ9.ZleIKGdHeUhx1ZYkekK5dnHm77h_BugcrxeOnsyiXD0",
"token_type":"Bearer",
"expires_in":3599
}
decodeすると以下のような感じです。
なんだか、OpenAM独自のパラメータがセットされていますね~。まぁいっか。
{
"at_hash": "ezK_0C3pEI61Ry02bP2sAw",
"sub": "johnd",
"auditTrackingId": "d7144481-6f77-4939-94d6-c5bf13c6b2dd-3521",
"iss": "https://iam.example.com:443/openam/oauth2",
"tokenName": "id_token",
"aud": "relyingparty",
"c_hash": "_1AU6xdR5EEEMYpoBJ_04Q",
"org.forgerock.openidconnect.ops": "aa5aeede-ad94-4b43-b0ab-1f45b167d616",
"azp": "relyingparty",
"auth_time": 1481292098,
"realm": "/",
"exp": 1481297166,
"tokenType": "JWTToken",
"iat": 1481293566
}
8. Relying PartyはID Tokenの検証をする
ID Tokenを受け取ったRelying Partyは本当にID Tokenが正しいかどうかを検証する必要があります(MUST)
詳しくはOpenID Connect CoreのID Token Validationを参照ください。
ざっくり検証ってどんなことするの?をピックアップすると、
- 公開鍵を使って署名検証する
- issがRelying Partyが期待していたOpenID Providerであるかどうか?
- audがRelying Party(自分)かどうか?
- iatが昔すぎないか?
- expがまだ切れていないか?
などなど。
こんだけの情報がRelying Partyに連携できると、その情報を信頼して**ほな、うちでも認証できたことにしたる!**ってできそうですね。(シングルサインオン)
〆
今日のまとめ
- OpenAMにRelying Partyの登録をすると、すぐにID Tokenを要求するフローを開始できる
- ID TokenはJSON Web Tokenとよばれる形式で表現される
- Authorization Code Flowはフロントで紐づけの情報(Authorization Code)を持ちまわって、バックチャネルでID連携する
明日こそRelying Partyのセットアップをする。
ばい!