はじめに
前回認証系のイメージ(概要)について調べた。
「じゃあこれで実装できるね!」とはならない。
実際のOIDCの仕組みなどを理解しないことには始まらない。
今回はそれを調べる
調査
OpenID Connectのざっくりフロー
- 実装にはサービスサーバーとIdPサーバーが必要っぽいな。
- 認証の暗号化とか難しいことは抜きにして、以下のフローで実装するっぽい
- クライアントがサービスに「外部IdPでログインしたい」と伝える
- サービスがIdPサーバーへのリダイレクトURLをクライアントに伝えて、リダイレクトさせる
- リダイレクト先のIdPサーバーが提示した認証と認可の画面でクライアントは許可する
- 許可されたIdPはサービスへのリダイレクトURLと認可コードを与えて、リダイレクトさせる
- 認可コードを受け取ったサービスはIdPサーバーに認可コードを伝える
- IdPサーバーがアクセストークンとIDトークンをサービスに返す
- このIDトークンを検証するために、IdPサーバーにアクセスして、鍵(クライアントシークレットor公開鍵)を入手する
- 検証ができたら、「IdPによりユーザーが認証された」ことを確認できる
OAuthのざっくりフロー
- 「ユーザー情報」を教えてくれるAPIがあるとする
- もし誰にでもユーザー情報を教えてくれるAPIだったら大変だ
- だから「悪意のないユーザー」であることを伝える必要がある
- そのために アクセストークン がある。「悪意がないよ!」と伝えるもの。
- そしてそのアクセストークンを発行するための 認可サーバー も必要。
- アクセストークンを発行する条件はユーザー認証
- このアクセストークンを発行する流れのプロトコルがOAuth
OpenID Connectのざっくりフロー (Part2)
- さっきのOAuthのフローとほぼ一緒
- だけど、発行するのは「アクセストークン」ではなく「IDトークン」
- つまりOIDCはOAuthを拡張したもの
- じゃあIDトークンってなに?
- ざっくりいうと、「ユーザーが認証されたという事実とそのユーザーの属性情報を、捏造されていないことを確認可能な方法で、各所に引き回すためにあるトークン」
IDトークンってなに?
eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ.ewogImlz cyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4 Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAi bi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEz MTEyODA5NzAsCiAibmFtZSI6ICJKYW5lIERvZSIsCiAiZ2l2ZW5fbmFtZSI6 ICJKYW5lIiwKICJmYW1pbHlfbmFtZSI6ICJEb2UiLAogImdlbmRlciI6ICJm ZW1hbGUiLAogImJpcnRoZGF0ZSI6ICIwMDAwLTEwLTMxIiwKICJlbWFpbCI6 ICJqYW5lZG9lQGV4YW1wbGUuY29tIiwKICJwaWN0dXJlIjogImh0dHA6Ly9l eGFtcGxlLmNvbS9qYW5lZG9lL21lLmpwZyIKfQ.rHQjEmBqn9Jre0OLykYNn spA10Qql2rvx4FsD00jwlB0Sym4NzpgvPKsDjn_wMkHxcp6CilPcoKrWHcip R2iAjzLvDNAReF97zoJqq880ZD1bwY82JDauCXELVR9O6_B0w3K-E7yM2mac AAgNCUwtik6SjoSUZRcf-O5lygIyLENx882p6MtmwaL1hd6qn5RZOQ0TLrOY u0532g9Exxcm-ChymrB4xLykpDj3lUivJt63eEGGN6DH5K6o33TcxkIjNrCD 4XB1CKKumZvCedgHHF3IAK4dVEDSUoGlH9z4pP_eWYNXvqQOjGs-rDaQzUHl 6cQQWNiDpWOl_lxXjQEvQ
- 「ヘッダー.ペイロード.署名」というふうに別れている
- この形式はJWS ( JSON Web Signature ) で定義されている
- この3つの部分はそれぞれbase64urlでエンコードされたもの。つまりデコードすると内容がわかる。
{"kid":"1e9gdk7","alg":"RS256"}
{ "iss": "http://server.example.com", "sub": "248289761001", "aud": "s6BhdRkqt3", "nonce": "n-0S6_WzA2Mj", "exp": 1311281970, "iat": 1311280970, "name": "Jane Doe", "given_name": "Jane", "family_name": "Doe", "gender": "female", "birthdate": "0000-10-31", "email": "janedoe@example.com", "picture": "http://example.com/janedoe/me.jpg" }
239 191 189 116 35 18 96 106 239 191 189 239 191 189 107 123 67 239 191 189 239 191 189 70 13 239 191 189 239 191 189 64 239 191 189 68 42 239 191 189 106 239 191 189 199 129 108 15 77 35 239 191 189 80 116 75 41 239 191 189 55 58 96 239 191 189 239 191 189 239 191 189 14 57 239 191 189 239 191 189 239 191 189 7 239 191 189 239 191 189 122 10 41 79 114 239 191 189 239 191 189 88 119 34 239 191 189 29 239 191 189 2 60 203 188 51 64 69 239 191 189 125 239 191 189 58 9 239 191 189 239 191 189 60 209 144 239 191 189 111 6 60 216 144 218 184 37 239 191 189 45 84 125 59 239 191 189 239 191 189 239 191 189 13 239 191 189 239 191 189 78 239 191 189 51 105 239 191 189 112 0 32 52 37 48 239 191 189 41 58 74 58 18 81 239 191 189 92 127 227 185 151 40 8 200 177 13 239 191 189 239 191 189 54 239 191 189 239 191 189 45 239 191 189 6 239 191 189 239 191 189 23 122 239 191 189 126 81 100 239 191 189 52 76 239 191 189 239 191 189 98 239 191 189 57 239 191 189 104 61 19 28 92 239 191 189 239 191 189 239 191 189 239 191 189 106 239 191 189 239 191 189 18 239 191 189 239 191 189 239 191 189 239 191 189 239 191 189 85 34 239 191 189 239 191 189 122 239 191 189 239 191 189 6 24 222 131 31 239 191 189 239 191 189 239 191 189 125 239 191 189 115 25 8 239 191 189 239 191 189 239 191 189 15 239 191 189 239 191 189 239 191 189 34 239 191 189 239 191 189 102 111 9 239 191 189 96 28 113 119 32 2 239 191 189 117 81 3 73 74 6 239 191 189 127 115 239 191 189 239 191 189 239 191 189 121 102 13 94 239 191 189 239 191 189 58 49 239 191 189 239 191 189 239 191 189 239 191 189 67 53 7 239 191 189 239 191 189 16 65 99 98 14 239 191 189 239 191 189 239 191 189 239 191 189 113 94 52 4 239 191 189 10
- 3つめの署名は1つめのヘッダーの
alg
の値によって暗号化方法が異なる。 - この署名が正しさを証明したいのは、「ヘッダー」と「ペイロード」の2つ
署名がない「ヘッダー.ペイロード.」という形式の Unsecured JWS というのもある
IDトークンには「ヘッダー.キー.初期ベクター.暗号文.認証タグ」という5つに別れた形式もある
- JWE (JSON Web Encryption) で定義している形式
- これはIDトークンを暗号化したいときに利用される
- OIDCの文脈だと4つめの暗号文の中に、「ヘッダー.ペイロード.署名」(つまりJWS)が入っている。
- 2段階に暗号化を行っている
- 対称性鍵暗号の共有鍵を生成する
- 平文を共有鍵で暗号化する
- 共有鍵を非対称鍵暗号の公開鍵で暗号化する
- 暗号化した平文と共有鍵を復号化する人に渡す
- 共有鍵を手元の秘密鍵で復号する
- 復号した共有鍵で暗号化した平文を復号化する
- 上記のフローに必要な情報はそれぞれ以下のように送られる
- では、非対称鍵暗号を使う場合は公開鍵を暗号化する側に渡さなければいけません。
その仕様がJWK ( JSON Web Key ) です。
- 使用はJWK Set Documentにかかれている
- JWKの集合が書かれている
- どう使うか、何に使ったほうがいいのか、などが書かれている
- これは、JWKの公開鍵をまとめたもの
- 使い方
- JWK Set Documentのありかの伝え方
- クライアントアプリの属性
jwks
の値としてJWK Set Document自体が登録されている - クライアントアプリの属性
jwks_uri
の値としてJWK Set Documentが置かれている場所が登録されている
- クライアントアプリの属性
- 使用はJWK Set Documentにかかれている
いままで説明してきた、暗号化する平文 ( JWSのペイロードや、JWEの暗号文など ) にも仕様があります。
- それが JWT ( JSON Web Token ) です。
{ "クレーム名": クレーム値, "クレーム名": クレーム値, ...... }
- ここまで話してきた内容を踏まえると、 「暗号化されたIDトークンは『JWSがJWEに含まれている形のNested JWT」 です
- 最後にIDトークンのクレームを見ていきましょう
iss
クレームsub
クレームsub
クレームはユーザーの一意識別子を表す- IDトークンは「ユーザー認証」された後発行するものなので、そのユーザーを表す値が
sub
クレームです
aud
クレームaud
クレームはこのJWTの受け取り手が誰であるべきかを表すもの- IDトークンの場合、発行を依頼したクライアントアプリのクライアントIDです。
exp
クレームiat
クレームauth_time
クレームnonce
クレームacr
クレームacr
クレームはユーザー認証が満たした認証コンテキスト ( パスワードによるシルバーレベルの認証、とか) のクラスを表す- 例) 値が "urn:mace:incommon:iap:silver" の場合、ユーザーはシルバーレベルの認証を受けたことを示します。
amr
クレームamr
クレームは認証手法を表している- パスワードベースの認証、ワンタイムパスワード、バイオメトリック認証など、さまざまな認証方法を示すことができます。
- 値が ["pwd", "mfa"] の場合、ユーザーはパスワード認証とマルチファクタ認証を併用して認証されたことを示します。
azp
クレームazp
クレームは認可された対象者を表しています。- つまり認可されたクライアントアプリケーションのクライアント ID の値です。
- このクレームは、ID トークンの発行を依頼したクライアントアプリケーションと認可されたクライアントアプリケーションが異なる場合、必要となります。
OAuthのクライアント認証とは
- クライアント認証は、クライアントIDとクライアントシークレットを用いて認証を行う
- クライアントID:アプリケーションの一意の識別子で、通常は公開情報
- クライアントシークレット:クライアント自身を証明する秘密鍵
※ あんまり概要をつかめなかったのでまた今度
OAuthの全フロー
- 4つの認可フローがある
- 認可コードフロー
- インプリシットフロー
- リソースオーナー・パスワード・クレデンシャルズフロー
- クライアント・クレデンシャルズフロー
- リフレッシュトークンフロー
OpenID Connectの全フロー
- OIDCはOAuth2を拡張したもの
- それぞれの発行する場所は 認可エンドポイント というところ。そこでresponse_typeを要求する
- code
- token
- id_token
- 以下はresponse_typeのによる場合わけした説明である
- response_type=code
- response_type=token
- インプリシットフローそのもの!scopeにopenidが含まれていようといまいと変わらない。
- つまりこれ、OIDCだと使わない。
- response_type=id_token
- response_type=id_token token
- response_type=code id_token
- response_type=code token (scopeにopenidがあり)
- response_type=code token (scopeにopenidがない)
- response_type=code id_token token
- response_type=none
- 認証・認可しても認可決定エンドポイントから何も発行しない。(何がしたいん?)
- 認可サーバー内の認可エンドポイントに認可リクエストを投げて「認証情報が入力できる認可画面」を表示してもらう
- 入力してもらったけど、何も返さない。
- 認証・認可しても認可決定エンドポイントから何も発行しない。(何がしたいん?)
あーーー疲れた。一旦ここまで。
また川崎さんにとてつもない感謝を。