第3回：Spring Security 5でサポートされたOAuth 2.0 Loginの処理の流れを(深く)理解する

前回「[Spring Security 5でサポートされるOAuth 2.0 Loginの大まかな処理の流れを理解する](https://qiita.com/kazuki43zoo/items/1e2d5892d9d3e9049fa9)」で大まかな処理の流れを紹介したので、それらをさらにブレークダウンした処理の流れなどをみていきたいと思います。

# 前提バージョン

* Spring Boot 2.0.0.M7
* Spring Security 5.0.0.RELEASE

# 認可要求

クライアントAPからプロバイダの認可画面を表示（＝正確にはクライアントからプロバイダへリダイレクト）する時の処理フローをみていきます。前回紹介した図だと、赤線で囲んだところがここでの説明対象になります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/7ca23af1-59bc-aeb9-fca7-c36366e69314.png)


認可要求(認可画面表示要求)が行われると、`OAuth2AuthorizationRequestRedirectFilter`を起点として、`ClientRegistrationRepository`、`AuthorizationRequestRepository`、`RedirectStrategy`などと連携して処理を行います。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/a76d23b2-456d-ded6-abe3-ae737e8f5998.png)


| 処理の流れ |
| --- |
| リソースオーナは、ログイン画面に表示されているリンクを押下して、「認可画面表示要求（`GET /oauth2/authorization/{registrationId}`）」を行う。デモアプリケーションではプロバイダにGitHubを利用しているので、`registrationId`は`github`になる。 |
| `OAuth2AuthorizationRequestRedirectFilter`は、`ClientRegistrationRepository`インタフェースの`findByRegistrationId`メソッドを呼び出して、引数に渡した`registrationId`に対応するクライアント登録情報(`ClientRegistration`)を参照する。 |
| `OAuth2AuthorizationRequestRedirectFilter`は、クライアント登録情報から「認可要求用のリクエスト情報(`OAuth2AuthorizationRequest`)」を生成し、`AuthorizationRequestRepository`の`saveAuthorizationRequest`メソッドを呼び出すことで、「認可要求用のリクエスト情報」をリクエストを跨いで共有できる領域（セッションなど）に保存する。（＝後述の「認可後の認証」の中で参照する） |
| `OAuth2AuthorizationRequestRedirectFilter`は、クライアント登録情報からプロバイダ提供の「認可エンドポイント」に移動するためのURLを生成し、`RedirectStrategy`を介してプロバイダ提供のページにリダイレクトする。 |

# 認可後の認証

プロバイダの認可画面で認可を行った後に認証する時の処理フローをみていきます。前回紹介した図だと、赤線で囲んだところがここでの説明対象になります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/ef4be1d6-6882-4ca0-e16f-c6b441ed5d3b.png)

認可が行われると、`OAuth2LoginAuthenticationFilter`を起点として、`ClientRegistrationRepository`、`AuthorizationRequestRepository`、`AuthenticationManager`、`AuthenticationProvider`などと連携して処理を行います。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/ff4aa158-1fe5-e4b4-21a9-a89c25c73c56.png)

ちょっと処理フローが長いので、「認証前処理」「認証処理」「認証後処理」の３つのフェーズに分けて説明したいと思います。

## 認証前処理

まず、認証処理に必要な情報を取集し、OAuth2ログイン用の認証トークン(`OAuth2LoginAuthenticationToken`)を生成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/49b95b42-83de-c592-300b-963a37c780d3.png)

| 処理の流れ |
| --- |
| リソースオーナは、認可画面に表示されている「認可ボタン」を押下してプロバイダのユーザ情報を使用してデモアプリケーションにログインすることを許可する。プロバイダ側での処理が完了すると、デモアプリケーションの「認可後の認証（`GET /login/oauth2/code/{registrationId}`）」処理が呼び出される（＝実際にはリダイレクトされる）。デモアプリケーションではプロバイダにGitHubを利用しているので、`registrationId`は`github`になる。 |
| `OAuth2LoginAuthenticationFilter`は、リクエストよりプロバイダからの認可応答を解析して「認可要求のレスポンス情報（`OAuth2AuthorizationResponse`）」を生成する。 |
| `OAuth2LoginAuthenticationFilter`は、`AuthorizationRequestRepository`インタフェースの`loadAuthorizationRequest`メソッドを呼び出して、「認可要求のリクエスト情報(`OAuth2AuthorizationRequest`)」を復元する。（復元した情報は`AuthorizationRequestRepository`インタフェースの`removeAuthorizationRequest`メソッドを呼び出して削除する） |
| `OAuth2LoginAuthenticationFilter`は、`ClientRegistrationRepository`インタフェースの`findByRegistrationId`メソッドを呼び出して、引数に渡した`registrationId`に対応する「クライアント登録情報(`ClientRegistration`)」を参照する。 |
| `OAuth2LoginAuthenticationFilter`は、「認可要求のリクエスト情報(`OAuth2AuthorizationRequest`)」「認可要求のレスポンス情報（`OAuth2AuthorizationResponse`）」「クライアント登録情報(`ClientRegistration`)」を保持する「OAuth2ログイン用の認証トークン(`OAuth2LoginAuthenticationToken`)」を生成する。 |

## 認証処理

つぎに、`AuthenticationManager`に「OAuth2ログイン用の認証トークン(`OAuth2LoginAuthenticationToken`)」を渡して認証処理をします実行（`authenticate`メソッドを呼び出す）。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/178ed920-23a7-b194-5ff9-fd25fa822417.png)

| 処理の流れ |
| --- |
| `OAuth2LoginAuthenticationFilter`は、`AuthenticationManager`インタフェースの`authenticate`メソッドに「OAuth2ログイン用の認証トークン(`OAuth2LoginAuthenticationToken`)」を渡して、リソースオーナの認証を行う。 |
| `AuthenticationManager`(実体は`ProviderManager`クラス)は、認証処理を`AuthenticationProvider`の`authenticate`メソッドへ委譲する。 |
| OAuth2 Login用の`AuthenticationProvider`インターフェースの実装クラスは、「トークンエンドポイント」「ユーザ情報エンドポイント」「JWKsエンドポイント（OIDC 1.0のみ）」にアクセスしてアクセストークンとユーザ情報を取得し、認証結果を返却する。（`AuthenticationProvider`の実装クラスは、OAuth 2.0用とOIDC 1.0用の２つのクラスが提供されています→詳細は後述します） |

## 認証後処理

さいごに、「認証結果トークン(`OAuth2LoginAuthenticationToken`)」をもとにリソースオーナをSpring Securiryの世界で認証済み（ログイン済み）の状態にします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/08ba34cf-9ee5-b55d-b1ff-74ad26fdebfb.png)

| 処理の流れ |
| --- |
| `OAuth2LoginAuthenticationFilter`は、認証結果から認証情報(`OAuth2AnthenticationToken`)を生成し、`SecurityContext`に設定する。SecurityContextに認証情報を設定することで、Spring Securiryの世界で認証済み（ログイン済み）の状態となる。 |
| `OAuth2LoginAuthenticationFilter`は、認証結果から「認証済みクライアント情報(`OAuth2AuthorizedClient`)」を生成し、`OAuth2AuthorizedClientService`の`saveAuthorizedClinet`メソッドを呼び出すことで、「認証済みクライアント情報」を任意のクラスからアクセスできる領域に保存する。（デモアプリケーションでは、`DemoController`の中から「認証済みクライアント情報」へアクセスしている） |

# AuthenticationProvider

Spring Securityは、ひとつのアプリケーション内で（同時に）複数の認証方式（例えば、フォームログイン、Basic認証、OAuth2.0 Login認証など）をサポートするための`AuthenticationManager`の実装クラスとして`ProviderManager`を提供しています。`ProviderManager`は、複数の`AuthenticationProvider`の実装クラスを保持し具体的な認証処理を`AuthenticationProvider`に委譲するスタイルを採用しており、OAuth 2.0/OIDC 1.0 Login用の`AuthenticationProvider`が提供されています。

```java:参考）AuthenticationProviderのメソッド定義
public interface AuthenticationProvider {

	// 認証処理を行うメソッド
	Authentication authenticate(Authentication authentication)
			throws AuthenticationException;

	// authenticateメソッドの引数に渡されるAuthenticationの型が
	// このクラスでサポートする認証方式か否かを判定するメソッド
	boolean supports(Class<?> authentication);

}
```

## OAuth 2.0用のAuthenticationProvider

Spring Securityは、OAuth 2.0をサポートするプロバイダで管理しているユーザでログインするための`AuthenticationProvider`の実装クラスとして`OAuth2LoginAuthenticationProvider`を提供しており、以下の条件をすべてみたす際にこのクラスで認証処理が行われます。

* `authenticate`メソッドの引数に渡される`Authentication`の型が`OAuth2LoginAuthenticationToken`に割り当てることができる
* 認可要求時のスコープに`openid`が含まれていない（→スコープに`openid`が含まれている場合はOIDC 1.0用の`AuthenticationProvider`で処理が行われる）

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/c5218e89-2389-c5d8-c32a-3ce828644951.png)

| 処理の流れ |
| --- |
| `OAuth2LoginAuthenticationProvider`は、`OAuth2AccessTokenResponseClient`インタフェースの`getTokenResponse`メソッドを呼び出すことで、「トークンエンドポイント」からアクセストークンを取得する。Spring Seuciryは、Nimbus OAuth 2.0 SDKのAPIを利用してアクセストークンを取得する実装クラス（`NimbusAuthorizationCodeTokenResponseClient`）を提供しています。 |
| `OAuth2LoginAuthenticationProvider`は、`OAuth2UserService`インタフェースの`loadUser`メソッドを呼び出すことで、「ユーザ情報ポイント（アクセストークンにひもづくユーザ情報を取得するAPI）」からユーザ情報を取得する。Spring Seuciryは、Nimbus OAuth 2.0 SDKのAPIを利用してアクセストークンを取得する実装クラス（`DefaultOAuth2UserService`）を提供しています。 |
| `OAuth2LoginAuthenticationProvider`は、認証結果を返却するために`OAuth2LoginAuthenticationToken`を生成する。 |


## OIDC 1.0用のAuthenticationProvider

Spring Securityは、OIDC 1.0をサポートするプロバイダで管理しているユーザでログインするための`AuthenticationProvider`の実装クラスとして`OidcAuthorizationCodeAuthenticationProvider`を提供しており、以下の条件をすべてみたす際にこのクラスで認証処理が行われます。

* `authenticate`メソッドの引数に渡される`Authentication`の型が`OAuth2LoginAuthenticationToken`に割り当てることができる
* 認可要求時のスコープに`openid`が含まれる

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/ddda40f1-a6a9-7828-4be4-900c6185b516.png)

| 処理の流れ |
| --- |
| `OidcAuthorizationCodeAuthenticationProvider`は、`OAuth2AccessTokenResponseClient`インタフェースの`getTokenResponse`メソッドを呼び出すことで、「トークンエンドポイント」からアクセストークン（＋IDトークン）を取得する。Spring Seuciryは、Nimbus OAuth 2.0 SDKのAPIを利用してアクセストークン（＋IDトークン）を取得する実装クラス（`NimbusAuthorizationCodeTokenResponseClient`）を提供しています。 |
| `OidcAuthorizationCodeAuthenticationProvider`は、`JwtDecorder`インタフェースの`decode`メソッドを呼び出すことで、IDトークンに含まれるクレーム（リソースオーナの識別子や認証情報など）を取得する。Spring Seuciryは、Nimbus OAuth 2.0 SDKのAPIを利用して「JWKsエンドポイント」からJWKを取得してIDトークンの検証＋デコードを行う実装クラス（`NimbusJwtDecoderJwkSupport`）を提供しています。 |
| `OidcAuthorizationCodeAuthenticationProvider`は、`OAuth2UserService`インタフェースの`loadUser`メソッドを呼び出すことで、「ユーザ情報ポイント」からユーザ情報（クレーム）を取得する。Spring Seuciryは、Nimbus OAuth 2.0 SDKのAPIを利用してアクセストークンを取得する実装クラス（`OidcUserService`）を提供しています。 |
| `OAuth2LoginAuthenticationProvider`は、認証結果を返却するために`OAuth2LoginAuthenticationToken`を生成する。 |


# 参考： OAuth 2.0/OIDC 1.0 Login機能で利用するモデルクラス

参考までに、本エントリーで紹介した処理フローの中で登場したモデルクラス（一部紹介してないクラスもあるけど・・）の構成を載せておきます。

> **NOTE:**
>
> モデルクラスっていう表現が正しいのかは全く自身なし・・・よい表現があったらコメントください！！

## 全体像

全体像の構成はこんな感じですが、ぱっと見だとよくわからないですね・・・ :sweat_smile: ということで・・・私の独断でこれらのクラスをいくつか分類してクラスの役割などを紹介します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/2cb1af42-af4d-bd27-b5d3-748866702eaa.png)


## クライアント登録情報

まずは・・・クライアント登録情報を起点としたクラス構成を紹介します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/d528e2a2-2b14-73f2-7396-44f49d5a4929.png)

| クラス名 | 説明 |
| ------ | ---- |
| `ClientRegistration` | プロバイダの各種エンドポイントにアクセスする際に必要となる「クライアント情報（クライアントID、クライアントシークレット、要求スコープ、認可後のリダイレクト先URIテンプレート）」「使用するグラントタイプ(認可コード vs インプリシット)」「トークンエンドポイントアクセス時の認証方式（Basic認証 vs Form認証）」などを保持する。 |
| `ProviderDetails` | プロバイダの各種エンドポイントのURIを保持する。 |
| `UserInfoEndpoint` | プロバイダ提供のユーザ情報を取得するエンドポイントに関する情報（URI、ユーザ識別子の属性名）を保持する。 |

> **NOTE:**
>
> Spring Securityは、「[Google](https://developers.google.com/identity/protocols/OpenIDConnect)」「[GitHub](https://developer.github.com/apps/building-integrations/setting-up-and-registering-oauth-apps/)」「[Facebook](https://developers.facebook.com/apps)」「[Okta](https://okta.github.io/authentication-guide/)」向けのクライアント登録情報を生成するためのサポートクラスとして[`CommonOAuth2Provider`](https://github.com/spring-projects/spring-security/blob/master/config/src/main/java/org/springframework/security/config/oauth2/client/CommonOAuth2Provider.java)を提供しています。


## OAuth 2.0認証処理用の認証トークン

つぎに・・・`AuthenticationManager`(`AuthenticationProvider`)の`authenticate`メソッドの引数と返り値として使用する「OAuth 2.0認証処理用の認証トークン」を起点としたクラス構成を紹介します。「OAuth 2.0認証処理用の認証トークン」は、認証依頼(`authenticate`メソッドの引数に渡す情報)と認証結果(`authenticate`メソッドの返り値として返却する情報)に分けてみていきます。

### 認証依頼

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/c8752696-7ba0-1f96-bcad-0417b2eee41e.png)

| クラス名 | 説明 |
| ------ | ---- |
| `OAuth2LoginAuthenticationToken` | OAuth 2.0認証処理で必要となる情報を「未認証の状態」で保持する。（＝他のオブジェクトへの参照を保持するだけのコンテナクラス） |
| `OAuth2AuthorizationExchange` | 認可要求と認可応答の情報を保持する。（＝他のオブジェクトへの参照を保持するだけのコンテナクラス） |
| `OAuth2AuthorizationRequest` | 認可要求の情報（「アクセスする認可エンドポイントのURI」「使用するグラントタイプ(認可コード vs インプリシット)」「使用する認可応答の種類（認可コード vs アクセストークン）」「認可後のリダイレクト先URI」「使用するスコープ一覧」「CSRF対策用に生成したステート値」「使用するクライアントID」など）を保持する。 |
| `OAuth2AuthorizationResponse` | 認可応答の情報（「リクエストURI=リダイレクト先URI」「プロバイダから戻されたステート値」「プロバイダが発行した認可コード」）を保持する。 |
| `OAuth2Error ` | 認可処理のエラー情報（「エラーコード」「エラー内容の説明」「エラー詳細にアクセスするためのURI」）を保持する。 |

### 認証結果

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/71b9bd10-eff4-105b-eb26-fe3c10efd51a.png)

| クラス名 | 説明 |
| ------ | ---- |
| `OAuth2LoginAuthenticationToken` | OAuth 2.0認証処理の処理結果を「認証済みの状態」で保持する。（＝他のオブジェクトへの参照を保持するだけのコンテナクラス） |
| `OAuth2AccessToken` | OAuth 2.0のアクセストークン情報（「トークン値」「トークン生成日時」「トークン有効期限日時」「トークンタイプ（Bearerのみサポート）」「スコープ一覧」）を保持する。 |
| `OAuth2User` | OAuth 2.0 Login機能を使用してOAuth 2.0のフローで認証したユーザであることを示す。 |
| `DefaultOAuth2User` | OAuth 2.0 Login機能を使用してOAuth 2.0のフローで認証したユーザのユーザ情報（「権限一覧（デフォルトだと「ROLE_USER」のみ）」「ユーザの属性情報」「ユーザ名（ユーザ識別子）を保持する属性名」）を保持する。 |
| `OAuth2UserAuthority` | OAuth 2.0 Login機能を使用してOAuth 2.0のフローで認証したユーザの権限情報（「権限値」「ユーザの属性情報」）を保持する。 |

ちなみに・・・OIDC 1.0の仕組みで認証した場合は、`OAuth2User`ではなく`OidcUser`を実装したクラスでユーザ情報を保持します。下の図をみてわかるとおり、OIDC 1.0フロー用はクラスはOAuth 2.0フロー用のクラスを継承しているため、OAuth 2.0フロー用のクラスで定義している情報も保持しています。

<img width="70%" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/117313/fabded30-58e9-eda6-7917-eecd478367f4.png">

| クラス名 | 説明 |
| ------ | ---- |
| `OidcUser` | OAuth 2.0 Login機能を使用してOIDC 1.0のフローで認証したユーザであることを示す。 |
| `DefaultOidcUser` | OAuth 2.0 Login機能を使用してOIDC 1.0のフローで認証したユーザのクレーム（「IDトークンの情報」「ユーザ情報（クレーム）」）を保持する。 |
| `OidcIdToken` | IDトークンにひもづく情報（「トークン値」「トークン生成日時」「トークン有効期限日時」「クレーム（ユーザ識別子、認証情報など）」）を保持する。 |
| `OidcUserInfo` | ユーザ情報エンドポイントから取得したクレーム（ユーザのプロフィールなど）を保持する。 |
| `OidcUserAuthority` | OAuth 2.0 Login機能を使用してOIDC 1.0のフローで認証したユーザのクレーム（「IDトークンの情報」「ユーザ情報（クレーム）」）を保持する。 |

> **NOTE:**
>
> OAuth 2.0ユーザ情報(`OAuth2User`や`OidcUser`）は、以下の方法でアクセスすることができます。ここでは`OAuth2User`にアクセスする方法を紹介しますが、基本的には`OidcUser`も同じ方法でアクセスすることができます。
> 
> * Controllerの引数に`OAuth2AuthenticationToken`を宣言して取得する
>
>     ```java
>     @GetMapping("/")
>     public String index(OAuth2AuthenticationToken authentication, Model model) {
>         OAuth2User user = authentication.getPrincipal();
>         // ...
>     }
>     ```
>
> * Controllerの引数に宣言（＋`@AuthenticationPrincipal`を付与）して取得する
>     Spring Bootの自動コンフィギュレーションを使えば特に何もしなくても`@AuthenticationPrincipal`が利用できますが、非Spring Boot環境の場合は、`@AuthenticationPrincipal`を利用できるようにBean定義を行う必要があります。詳しくは、[Spring Seucirtyのリファレンス](https://docs.spring.io/spring-security/site/docs/5.0.1.BUILD-SNAPSHOT/reference/htmlsingle/#mvc-authentication-principal)をみてください。
>
>     ```java
>     @GetMapping("/attributes")
>     public String userAttributeAtLogin(@AuthenticationPrincipal OAuth2User oauth2User, Model model) {
>         // ...
>     }
>     ```
>
> * `SecurityContextHolder`(`SecurityContext`)のメソッドを呼び出して取得する
>
>     ```java
>     OAuth2User user =
>         OAuth2AuthenticationToken.class.cast(SecurityContextHolder.getContext().getAuthentication())
>         .getPrincipal();
>     ```


## OAuth 2.0認証情報

つぎに・・・`AuthenticationManager`(`AuthenticationProvider`)の`authenticate`メソッドを呼び出して認証が成功した後に生成する「OAuth 2.0認証情報」を起点としたクラス構成を紹介します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/8053ffb2-99db-f4d7-5af0-a643b18162e0.png)

| クラス名 | 説明 |
| ------ | ---- |
| `OAuth2AuthenticationToken` | OAuth 2.0 Login機能で認証した際の認証情報（「権限一覧」「ユーザ情報」「使用したクライアント登録情報のID」）を保持する。|

> **NOTE:**
>
> OAuth 2.0認証情報は、以下の方法でアクセスすることができます。
> 
> * Controllerの引数に宣言して受け取る
>
>     ```java
>     @GetMapping("/")
>     public String index(OAuth2AuthenticationToken authentication, Model model) {
>         // ...
>     }
>     ```

> * `SecurityContextHolder`(`SecurityContext`)のメソッドを呼び出して取得する
>
>     ```java
>     OAuth2AuthenticationToken authentication =
>         OAuth2AuthenticationToken.class.cast(SecurityContextHolder.getContext().getAuthentication());
>     ```

## OAuth 2.0認証済みクライアント情報

最後に・・・`AuthenticationManager`(`AuthenticationProvider`)の`authenticate`メソッドを呼び出して認証が成功した後に生成する「OAuth 2.0認証済みクライアント情報」を起点としたクラス構成を紹介します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/117313/1819d8f0-f407-bfb5-f098-2d91b37258ae.png)

| クラス名 | 説明 |
| ------ | ---- |
| `OAuth2AuthorizedClient` | OAuth 2.0 Login機能で認証した際のクライアント情報（「使用したクライアント登録情報」「ユーザ名（ユーザの識別子）」「プロバイダから取得したアクセストークン」）を保持する。|

> **NOTE:**
>
> OAuth 2.0認証済みクライアント情報は、`OAuth2AuthorizedClientService`の`loadAuthorizedClient`メソッドを呼び出すことで取得することができます。
> 
> ```java
> @Controller
> public class DemoController {
>
>     private final RestOperations restOperations = new RestTemplate();
>     private final OAuth2AuthorizedClientService authorizedClientService;
>
>     public DemoController(OAuth2AuthorizedClientService authorizedClientService) {
>         this.authorizedClientService = authorizedClientService; // OAuth2AuthorizedClientServiceをインジェクション
>     }
>
>     @GetMapping("/attributes/latest")
>     public String userLatestAttribute(OAuth2AuthenticationToken authentication, Model model) {
>         // loadAuthorizedClientの引数にOAuth 2.0認証情報で保持している「クライアント登録情報ID」と「ユーザ名」を渡す
>         OAuth2AuthorizedClient authorizedClient =
>                this.authorizedClientService.loadAuthorizedClient(
>                        authentication.getAuthorizedClientRegistrationId(),
>                        authentication.getName());
>
>         // RestTemplateを使用し、アクセストークンを付与してユーザ情報エンドポイントへアクセスする
>         String userInfoUri = authorizedClient.getClientRegistration()
>                 .getProviderDetails().getUserInfoEndpoint().getUri();
>         RequestEntity<Void> requestEntity = RequestEntity.get(URI.create(userInfoUri))
>                 .header(HttpHeaders.AUTHORIZATION, "Bearer " + authorizedClient.getAccessToken().getTokenValue())
>                 .build();
>
>         // ...
>     }
>
> }
> ````
> 
> ちなみに・・・ここでは`RestTemplate`を使用してユーザ情報エンドポイントへアクセスする例になっていますが、Spring Framework 5で追加された「[`WebClient`(リアクティブ対応のHTTPクライアント)](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-client)」を使うこともできます。
>
> ```java
> Map userAttributes = WebClient.builder()
>         .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + authorizedClient.getAccessToken().getTokenValue())
>         .build()
>         .get()
>         .uri(userInfoUri)
>         .retrieve()
>         .bodyToMono(Map.class)
>         .block();
> model.addAttribute("attributes", userAttributes);
> ```

#

# まとめ

[デモアプリケーション](https://qiita.com/kazuki43zoo/items/658eabbf0d6a60d3ed89)を例に、本エントリも含めて２回にわけてSpring Security 5で追加されたOAuth 2.0/OIDC 1.0 Login機能の仕組み（処理フローなど）をある程度細かくみてきました。本エントリーでは説明を省いた部分もあるので、もっと細かく（実装レベルの）処理フローを知りたい方は、本エントリーを足がかりに？Spring Securityのソースコードを読んでいただければと思います！
次回は・・・Bean定義（自動コンフィギュレーション）やデフォルト動作のカスタマイズ方法などを紹介できればな〜と思っていますが、いつになるかわかりません :smirk:

# 参考サイト

* https://github.com/spring-projects/spring-security/tree/master/samples/boot/oauth2login
* https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#jc-oauth2login
* https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2login-advanced
* https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#mvc-authentication-principal