OpenID Connect 全フロー解説

# **はじめに**

**OpenID Connect** は **OAuth 2.0** を拡張する形で策定されました。

OAuth 2.0 は**アクセストークン**発行手順に関する仕様で、**[RFC 6749](https://tools.ietf.org/html/rfc6749)（The OAuth 2.0 Authorization Framework）**で定義されています（参考：[一番分かりやすい OAuth の説明](https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be)）。

一方、OpenID Connect は **ID トークン**発行手順に関する仕様で、主要部分は **[OpenID Connect Core 1.0](http://openid.net/specs/openid-connect-core-1_0.html)** で定義されています（参考：[一番分かりやすい OpenID Connect の説明](https://qiita.com/TakahikoKawasaki/items/498ca08bbfcc341691fe#_reference-9686d2988451466463eb)）。

RFC 6749 は**[認可エンドポイント](https://tools.ietf.org/html/rfc6749#section-3.1 "RFC 6749, 3.1. Authorization Endpoint")**という Web API を定義しています。 この API は必須のリクエストパラメーターとして **`response_type`** を要求します。 OpenID Connect は、この `response_type` の仕様を拡張することにより ID トークン発行手順を定義しました。

RFC 6749 の時点では `response_type` が取りうる値は `code` か `token` のどちらかでしたが、OpenID Connect では、`id_token` という新しい値を追加した上で、**`code`、`token`、`id_token` の任意の組み合わせ**を `response_type` に指定してもよいことにしました。 加えて、**`none`** という特殊な値も定義しました。 この結果、`response_type` は次の値を取りうることになりました。

||`response_type`|
|:-:|:--|
|1|`code`|
|2|`token`|
|3|`id_token`|
|4|`id_token token`|
|5|`code id_token`|
|6|`code token`|
|7|`code id_token token`|
|8|`none`|

以降、`response_type` の値ごとにフローを紹介します。

なお、ID トークンの発行を要求するリクエストは、`scope` リクエストパラメーターに `openid` という値を含める必要があります。 特に、`scope` に `openid` を含めていないと、`response_type=code` のケースは RFC 6749 の[認可コードフロー](https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f#1-認可コードフロー)そのものとして扱われ、ID トークンは発行されないので注意してください。 `response_type=code token` についても同様のことが言えます。


# **1. response_type=code**

`response_type` の値が `code` の場合、これだけでは RFC 6749 の[認可コードフロー](https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f#1-認可コードフロー)と何も変わりません。 しかし、`scope` に `openid` が含まれている場合、トークンエンドポイントから、アクセストークンに加えて ID トークンも発行されます。

**`scope` に `openid` が含まれる場合**

||認可コード|アクセストークン|ID トークン|
|:--|:-:|:-:|:-:|
|認可エンドポイント|発行|Ｘ|Ｘ|
|トークンエンドポイント|Ｘ|発行|発行|

![code+openid.png](https://qiita-image-store.s3.amazonaws.com/0/106044/78829775-3a31-e2e6-bd06-821716169d8f.png)

**`scope` に `openid` が含まれない場合**（RFC 6749 の[認可コードフロー](https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f#1-認可コードフロー)）

||認可コード|アクセストークン|ID トークン|
|:--|:-:|:-:|:-:|
|認可エンドポイント|発行|Ｘ|Ｘ|
|トークンエンドポイント|Ｘ|発行|Ｘ|

![code-openid.png](https://qiita-image-store.s3.amazonaws.com/0/106044/d553ccb5-ef5a-15a6-cce5-0be19186aef9.png)


# **2. response_type=token**

`response_type` の値が `token` の場合、これは RFC 6749 の[インプリシットフロー](https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f#2-インプリシットフロー)そのものです。 `scope` に `openid` が含まれていたとしても ID トークンは発行されません。

||認可コード|アクセストークン|ID トークン|
|:--|:-:|:-:|:-:|
|認可エンドポイント|Ｘ|発行|Ｘ|

![token.png](https://qiita-image-store.s3.amazonaws.com/0/106044/f574dd80-6d6b-8ba3-2b14-3cf45174cd9a.png)

OpenID Connect では `token` という応答タイプを使わないことは、OpenID Connect Core 1.0 の「[3. Authentication](http://openid.net/specs/openid-connect-core-1_0.html#Authentication)」の末尾に明示的に書かれています。

> _NOTE: While OAuth 2.0 also defines the **`token`** Response Type value for the Implicit Flow, **OpenID Connect does not use this Response Type**, since no ID Token would be returned._


# **3. response_type=id_token**

`response_type` が `id_token` の場合、認可エンドポイントから ID トークンが発行されます。 このフローではトークンエンドポイントは使われません。

||認可コード|アクセストークン|ID トークン|
|:--|:-:|:-:|:-:|
|認可エンドポイント|Ｘ|Ｘ|発行|

![id_token.png](https://qiita-image-store.s3.amazonaws.com/0/106044/525418dd-9c9a-1c81-78c7-80a765beb185.png)


# **4. response_type=id_token token**

`response_type` が `id_token token` の場合、認可エンドポイントからアクセストークンと ID トークンが発行されます。 このフローではトークンエンドポイントは使われません。

||認可コード|アクセストークン|ID トークン|
|:--|:-:|:-:|:-:|
|認可エンドポイント|Ｘ|発行|発行|

![id_token_token.png](https://qiita-image-store.s3.amazonaws.com/0/106044/fe5dd92f-e94e-8412-1dc7-b11def7e80e3.png)

認可エンドポイントから ID トークンと共にアクセストークンを発行する場合、ある方法で計算されたアクセストークンのハッシュ値を ID トークンに埋め込む必要があるので、このフローを実装する際は注意してください。 OpenID Connect Core 1.0 の「[3.2.2.10. ID Token](http://openid.net/specs/openid-connect-core-1_0.html#ImplicitIDToken)」には次のように書かれています。

>**at_hash**
>
><i>Access Token hash value. Its value is the base64url encoding of the left-most half of the hash of the octets of the ASCII representation of the `access_token` value, where the hash algorithm used is the hash algorithm used in the `alg` Header Parameter of the ID Token's JOSE Header. For instance, if the `alg` is `RS256`, hash the `access_token` value with SHA-256, then take the left-most 128 bits and base64url encode them. The `at_hash` value is a case sensitive string.</i>
>
><i>**If the ID Token is issued from the Authorization Endpoint with an `access_token` value, which is the case for the `response_type` value `id_token token`, this is REQUIRED**; it MAY NOT be used when no Access Token is issued, which is the case for the `response_type` value `id_token`.</i>


# **5. response_type=code id_token**

`response_type` が `code id_token` の場合、認可エンドポイントから認可コードと ID トークンが、トークンエンドポイントからアクセストークンと ID トークンが発行されます。

||認可コード|アクセストークン|ID トークン|
|:--|:-:|:-:|:-:|
|認可エンドポイント|発行|Ｘ|発行|
|トークンエンドポイント|Ｘ|発行|発行|

![code_id_token.png](https://qiita-image-store.s3.amazonaws.com/0/106044/92d7bca9-7b90-b81b-5a99-eca7c8bacc16.png)

認可エンドポイントとトークンエンドポイントの両方から ID トークンが発行されますが、双方の ID トークンの内容は必ずしも同じとは限りません。 これについて OpenID Connect Core 1.0 の「[3.3.3.6 ID Token](http://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken2)」で次のように述べられています。

> <i>If an ID Token is returned from both the Authorization Endpoint and from the Token Endpoint, which is the case for the `response_type` values `code id_token` and `code id_token token`, the `iss` and `sub` Claim Values MUST be identical in both ID Tokens. All Claims about the Authentication event present in either SHOULD be present in both. If either ID Token contains Claims about the End-User, any that are present in both SHOULD have the same values in both. Note that **the OP MAY choose to return fewer Claims about the End-User from the Authorization Endpoint, for instance, for privacy reasons**. The `at_hash` and `c_hash` Claims MAY be omitted from the ID Token returned from the Token Endpoint even when these Claims are present in the ID Token returned from the Authorization Endpoint, because the ID Token and Access Token values returned from the Token Endpoint are already cryptographically bound together by the TLS encryption performed by the Token Endpoint.</i>

認可エンドポイントから ID トークンと共に認可コードを発行する場合、ある方法で計算された認可コードのハッシュ値を ID トークンに埋め込む必要があるので、このフローを実装する際は注意してください。 OpenID Connect Core 1.0 の「[3.3.2.11. ID Token](http://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken)」には次のように書かれています。

>**c_hash**
>
><i>Code hash value. Its value is the base64url encoding of the left-most half of the hash of the octets of the ASCII representation of the `code` value, where the hash algorithm used is the hash algorithm used in the `alg` Header Parameter of the ID Token's JOSE Header. For instance, if the `alg` is `HS512`, hash the `code` value with SHA-512, then take the left-most 256 bits and base64url encode them. The `c_hash` value is a case sensitive string.</i>
>
><i>**If the ID Token is issued from the Authorization Endpoint with a `code`, which is the case for the `response_type` values `code id_token` and `code id_token token`, this is REQUIRED**; otherwise, its inclusion is OPTIONAL.</i>


# **6. response_type=code token**

`response_type` が `code token` の場合、認可エンドポイントから認可コードとアクセストークンが発行され、トークエンドポイントからアクセストークンが発行されます。 加えて、`scope` に `openid` が含まれる場合は、トークンエンドポイントから ID トークンも発行されます。

**`scope` に `openid` が含まれる場合**

||認可コード|アクセストークン|ID トークン|
|:--|:-:|:-:|:-:|
|認可エンドポイント|発行|発行|Ｘ|
|トークンエンドポイント|Ｘ|発行|発行|

![code_token+openid.png](https://qiita-image-store.s3.amazonaws.com/0/106044/1e385f9a-3614-f873-d0b5-034f995cfc60.png)

**`scope` に `openid` が含まれない場合**

||認可コード|アクセストークン|ID トークン|
|:--|:-:|:-:|:-:|
|認可エンドポイント|発行|発行|Ｘ|
|トークンエンドポイント|Ｘ|発行|Ｘ|

![code_token-openid.png](https://qiita-image-store.s3.amazonaws.com/0/106044/8f2ade60-d26a-e839-7cd0-3b2fda47fb78.png)

認可エンドポイントとトークンエンドポイントの両方からアクセストークンが発行されますが、双方のアクセストークンは必ずしも同じとは限りません。 これについて OpenID Connect Core 1.0 の「[3.3.3.8. Access Token](http://openid.net/specs/openid-connect-core-1_0.html#HybridAccessToken2)」に次のように書かれています。

><i>If an Access Token is returned from both the Authorization Endpoint and from the Token Endpoint, which is the case for the `response_type` values `code token` and `code id_token token`, their values MAY be the same or they MAY be different. Note that **different Access Tokens might be returned be due to the different security characteristics of the two endpoints and the lifetimes and the access to resources granted by them might also be different**.</i>


# **7. response_type=code id_token token**

`response_type` が `code id_token token` の場合、認可エンドポイントから認可コード、アクセストークン、ID トークンが発行され、トークエンドポイントからアクセストークンと ID トークンが発行されます。

||認可コード|アクセストークン|ID トークン|
|:--|:-:|:-:|:-:|
|認可エンドポイント|発行|発行|発行|
|トークンエンドポイント|Ｘ|発行|発行|

![code_id_token_token.png](https://qiita-image-store.s3.amazonaws.com/0/106044/e1993229-1caa-58b1-37f6-4f67af95980b.png)

認可エンドポイントとトークンエンドポイントの両方からアクセストークンが発行されますが、双方のアクセストークンは必ずしも同じとは限りません。 これは「6. response_type=code token」と同様です。 仕様については OpenID Connect Core 1.0 の「[3.3.3.8. Access Token](http://openid.net/specs/openid-connect-core-1_0.html#HybridAccessToken2)」を参照してください。

認可エンドポイントから ID トークンを発行する際、アクセストークンも共に発行する場合は当該アクセストークンのハッシュ値を、認可コードも共に発行する場合は当該認可コードのハッシュ値を、ID トークンに埋め込む必要があります。 これは「4. response_type=id_token token」や「5. response_type=code id_token」と同様です。 仕様については OpenID Connect Core 1.0 の「[3.3.2.11. ID Token](http://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken)」を参照してください。


# **8. response_type=none**

`response_type` が `none` の場合、認可エンドポイントからは何も発行されません。 このフローではトークンエンドポイントは使われません。

||認可コード|アクセストークン|ID トークン|
|:--|:-:|:-:|:-:|
|認可エンドポイント|Ｘ|Ｘ|Ｘ|

![none.png](https://qiita-image-store.s3.amazonaws.com/0/106044/45f47452-4e2d-ce08-e742-b471f5f37556.png)

`none` の定義自体は [OAuth 2.0 Multiple Response Type Encoding Practices](http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html) の「[4. None Response Type](http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#none)」にあります。


# **9. サポート状況**

OpenID Connect サポートを謳っているソフトウェアが上記全てのフローをサポートしているとは限りません。 [OpenID Certification](http://openid.net/certification/) 取得済みの OpenID プロバイダーの実装に限っても、約半数は Hybrid OP プロファイルを取得していないので（＝ハイブリッドフローをサポートしていないので）、全フローをサポートしている OpenID プロバイダーの実装はむしろ少数派と考えてよいでしょう。

[OpenID Foundation](http://openid.net/) の [Financial API ワーキンググループ](http://openid.net/wg/fapi/)が仕様策定中の Financial API では、更新系 API を利用するためのアクセストークンを取得するためには、`response_type` の値を `code id_token` もしくは `code id_token token` として認可リクエストを投げなければなりません。 [Financial Services – Financial API - Part 2: Read and Write API Security Profile](http://openid.net/specs/openid-financial-api-part-2.html) の「[5.2.2. Authorization Server](http://openid.net/specs/openid-financial-api-part-2.html#authorization-server)」）には次のように書かれています。

> <i>shall require the `response_type` values `code id_token` or `code id_token token`;</i>

これはつまり、Financial API への準拠を考えているのであれば、OpenID Connect のハイブリッドフローをサポートしている認可サーバーを使用する必要があるということです。 世の中には `response_type=code` しかサポートしていない OpenID Connect 実装も実在するので、認可サーバー選択の際は注意してください。

**追記：2019 年 4 月 20 日**
2018 年 10 月に承認された FAPI 第二版では、**JARM** を使う場合、ID トークンを Detached Signature として用いる必要がなくなるため、更新系 API 用アクセストークンを要求する場合でも、`response_type` に `id_token` を含めなくてもよくなります。詳細は『[【2019年版】世界最先端の API セキュリティー技術、実装者による『FAPI（Financial-grade API）』解説](https://qiita.com/TakahikoKawasaki/items/83c47c9830097dba2744)』の『[2.5. Part 2: JARM](https://qiita.com/TakahikoKawasaki/items/83c47c9830097dba2744#25-part-2-jarm)』を参照してください。


# **おわりに**

こんな複雑なもの実装していられない、と思った方は [Authlete](https://www.authlete.com/)（オースリート）の利用をご検討ください！（参考：[OAuth 2.0 / OIDC 実装の新アーキテクチャー](https://qiita.com/TakahikoKawasaki/items/b2a4fc39e0c1a1949aab)）

**追記：2020 年 3 月 20 日**
この記事の内容を含む、筆者本人による『**[OAuth & OIDC 入門編](https://www.youtube.com/watch?v=PKPj_MmLq5E)**』解説動画を公開しました！