認可付与サポート

このセクションでは、Spring Security による認可付与のサポートについて説明します。

認証コード

認証コード [IETF] (英語) 付与の詳細については、OAuth 2.0 認証フレームワークを参照してください。

認可の取得

認可コードの付与については、認可リクエスト / レスポンス [IETF] (英語) プロトコルフローを参照してください。

認可リクエストの開始

OAuth2AuthorizationRequestRedirectFilter は OAuth2AuthorizationRequestResolver を使用して OAuth2AuthorizationRequest を解決し、エンドユーザーのユーザーエージェントを認可サーバーの認可エンドポイントにリダイレクトすることで認可コード付与フローを開始します。

OAuth2AuthorizationRequestResolver の主なロールは、提供された Web リクエストから OAuth2AuthorizationRequest を解決することです。デフォルトの実装 DefaultOAuth2AuthorizationRequestResolver は、(デフォルトの)パス /oauth2/authorization/{registrationId} で一致し、registrationId を抽出し、それを使用して、関連付けられた ClientRegistration の OAuth2AuthorizationRequest を構築します。

OAuth 2.0 クライアント登録の次の Spring Boot プロパティを検討してください。

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/authorized/okta"
            scope: read, write
        provider:
          okta:
            authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

上記のプロパティを指定すると、ベースパス /oauth2/authorization/okta を使用したリクエストは、OAuth2AuthorizationRequestRedirectFilter による認証リクエストリダイレクトを開始し、最終的に認証コード付与フローを開始します。

AuthorizationCodeOAuth2AuthorizedClientProvider は、認可コード付与のための OAuth2AuthorizedClientProvider の実装であり、OAuth2AuthorizationRequestRedirectFilter による認可リクエストリダイレクトも開始します。

OAuth 2.0 クライアントがパブリッククライアント [IETF] (英語) の場合、OAuth 2.0 クライアントの登録を次のように構成します。

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-authentication-method: none
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/authorized/okta"
            # ...

パブリッククライアントは、コード交換用の証明キー [IETF] (英語) (PKCE)を使用してサポートされます。クライアントが信頼できない環境(ネイティブアプリケーションや Web ブラウザーベースのアプリケーションなど)で実行されているため、資格情報の機密性を維持できない場合、次の条件が満たされると PKCE が自動的に使用されます。

  1. client-secret は省略(または空)されており

  2. client-authentication-method は none に設定されます (ClientAuthenticationMethod.NONE)

または

  1. ClientRegistration.clientSettings.requireProofKey が true の場合 (この場合、ClientRegistration.authorizationGrantType は authorization_code である必要があります。)

OAuth 2.0 プロバイダーが機密クライアント [IETF] (英語) の PKCE をサポートしている場合は、(オプションで) DefaultOAuth2AuthorizationRequestResolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce()) を使用して構成できます。

DefaultOAuth2AuthorizationRequestResolver は、UriComponentsBuilder を使用して、redirect-uri の URI テンプレート変数もサポートします。

次の構成では、サポートされているすべての URI テンプレート変数を使用します。

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            # ...
            redirect-uri: "{baseScheme}://{baseHost}{basePort}{basePath}/authorized/{registrationId}"
            # ...

{baseUrl} は {baseScheme}://{baseHost}{basePort}{basePath} に解決されます

URI テンプレート変数を使用して redirect-uri を構成すると、OAuth 2.0 クライアントがプロキシサーバーの背後で実行されている場合に特に役立ちます。そうすることで、redirect-uri を展開するときに X-Forwarded-* ヘッダーが使用されるようになります。

認可リクエストのカスタマイズ

OAuth2AuthorizationRequestResolver が実現できる主な使用例の 1 つは、OAuth 2.0 認可フレームワークで定義された標準パラメーターを超える追加パラメーターで認可リクエストをカスタマイズする機能です。

例: OpenID Connect は、OAuth 2.0 認証フレームワーク [IETF] (英語) で定義された標準パラメーターから拡張された、認証コードフロー (英語) の追加の OAuth 2.0 リクエストパラメーターを定義します。これらの拡張パラメーターの 1 つは prompt パラメーターです。

prompt パラメーターはオプションです。スペースで区切られた大文字と小文字を区別する ASCII 文字列値のリスト。認可サーバーがエンドユーザーに再認証と同意を求めるかどうかを指定します。定義された値は次のとおりです: noneloginconsentselect_account

以下の例は、リクエストパラメーター prompt=consent を含めることにより、oauth2Login() の認可リクエストをカスタマイズする Consumer<OAuth2AuthorizationRequest.Builder> で DefaultOAuth2AuthorizationRequestResolver を構成する方法を示しています。

  • Java

  • Kotlin

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {

	@Autowired
	private ClientRegistrationRepository clientRegistrationRepository;

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.anyRequest().authenticated()
			)
			.oauth2Login((oauth2) -> oauth2
				.authorizationEndpoint((authorization) -> authorization
					.authorizationRequestResolver(
						authorizationRequestResolver(this.clientRegistrationRepository)
					)
				)
			);
		return http.build();
	}

	private OAuth2AuthorizationRequestResolver authorizationRequestResolver(
			ClientRegistrationRepository clientRegistrationRepository) {

		DefaultOAuth2AuthorizationRequestResolver authorizationRequestResolver =
				new DefaultOAuth2AuthorizationRequestResolver(
						clientRegistrationRepository, "/oauth2/authorization");
		authorizationRequestResolver.setAuthorizationRequestCustomizer(
				authorizationRequestCustomizer());

		return  authorizationRequestResolver;
	}

	private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
		return customizer -> customizer
					.additionalParameters((params) -> params.put("prompt", "consent"));
	}
}
@Configuration
@EnableWebSecurity
class SecurityConfig {

    @Autowired
    private lateinit var customClientRegistrationRepository: ClientRegistrationRepository

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            authorizeHttpRequests {
                authorize(anyRequest, authenticated)
            }
            oauth2Login {
                authorizationEndpoint {
                    authorizationRequestResolver = authorizationRequestResolver(customClientRegistrationRepository)
                }
            }
        }
        return http.build()
    }

    private fun authorizationRequestResolver(
            clientRegistrationRepository: ClientRegistrationRepository?): OAuth2AuthorizationRequestResolver {
        val authorizationRequestResolver = DefaultOAuth2AuthorizationRequestResolver(
                clientRegistrationRepository, "/oauth2/authorization")
        authorizationRequestResolver.setAuthorizationRequestCustomizer(
                authorizationRequestCustomizer())
        return authorizationRequestResolver
    }

    private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
        return Consumer { customizer ->
            customizer
                    .additionalParameters { params -> params["prompt"] = "consent" }
        }
    }
}

追加のリクエストパラメーターが特定のプロバイダーで常に同じであるという単純なユースケースの場合、authorization-uri プロパティに直接追加できます。

例: リクエストパラメーター prompt の値がプロバイダー okta の常に consent である場合、次のように構成できます。

spring:
  security:
    oauth2:
      client:
        provider:
          okta:
            authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize?prompt=consent

上記の例は、標準パラメーターの上にカスタムパラメーターを追加する一般的なユースケースを示しています。または、要件がより高度な場合は、OAuth2AuthorizationRequest.authorizationRequestUri プロパティをオーバーライドすることで、認可リクエスト URI の構築を完全に制御できます。

OAuth2AuthorizationRequest.Builder.build() は OAuth2AuthorizationRequest.authorizationRequestUri を構築します。OAuth2AuthorizationRequest.authorizationRequestUri は、application/x-www-form-urlencoded 形式を使用するすべてのクエリパラメーターを含む認証リクエスト URI を表します。

次の例は、前の例からの authorizationRequestCustomizer() のバリエーションを示しており、代わりに OAuth2AuthorizationRequest.authorizationRequestUri プロパティをオーバーライドします。

  • Java

  • Kotlin

private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
	return customizer -> customizer
				.authorizationRequestUri((uriBuilder) -> uriBuilder
					.queryParam("prompt", "consent").build());
}
private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
    return Consumer { customizer: OAuth2AuthorizationRequest.Builder ->
        customizer
                .authorizationRequestUri { uriBuilder: UriBuilder ->
                    uriBuilder
                            .queryParam("prompt", "consent").build()
                }
    }
}

認証リクエストの保存

AuthorizationRequestRepository は、認可リクエストが開始されてから認可レスポンスが受信されるまで(コールバック)、OAuth2AuthorizationRequest の永続化を担当します。

OAuth2AuthorizationRequest は、認可レスポンスを関連付けて検証するために使用されます。

AuthorizationRequestRepository のデフォルトの実装は HttpSessionOAuth2AuthorizationRequestRepository で、HttpSession に OAuth2AuthorizationRequest を格納します。

AuthorizationRequestRepository のカスタム実装がある場合は、次のように構成できます。

AuthorizationRequestRepository の設定
  • Java

  • Kotlin

  • XML

@Configuration
@EnableWebSecurity
public class OAuth2ClientSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Client((oauth2) -> oauth2
				.authorizationCodeGrant((codeGrant) -> codeGrant
					.authorizationRequestRepository(this.authorizationRequestRepository())
					// ...
				)
			)
            .oauth2Login((oauth2) -> oauth2
                .authorizationEndpoint((endpoint) -> endpoint
                    .authorizationRequestRepository(this.authorizationRequestRepository())
                    // ...
                )
            );
			return http.build();
	}

    @Bean
    public AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository() {
        return new CustomOAuth2AuthorizationRequestRepository();
    }
}
@Configuration
@EnableWebSecurity
class OAuth2ClientSecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Client {
                authorizationCodeGrant {
                    authorizationRequestRepository = authorizationRequestRepository()
                }
            }
        }
        return http.build()
    }
}
<http>
	<oauth2-client>
		<authorization-code-grant authorization-request-repository-ref="authorizationRequestRepository"/>
	</oauth2-client>
</http>

アクセストークンのリクエスト

認可コードの付与については、アクセストークンリクエスト / レスポンス [IETF] (英語) プロトコルフローを参照してください。

認証コード付与の OAuth2AccessTokenResponseClient のデフォルトの実装は RestClientAuthorizationCodeTokenResponseClient です。これは、RestClient インスタンスを使用して、認証サーバーのトークンエンドポイントでアクセストークンの認証コードを交換します。

RestClientAuthorizationCodeTokenResponseClient は非常に柔軟性が高く、認可コード付与に対する OAuth 2.0 アクセストークンのリクエストとレスポンスをカスタマイズするためのオプションをいくつか提供します。詳細については、次のユースケースから選択してください。

アクセストークンリクエストのカスタマイズ

RestClientAuthorizationCodeTokenResponseClient は、OAuth 2.0 アクセストークンリクエストの HTTP ヘッダーとリクエストパラメーターをカスタマイズするためのフックを提供します。

リクエストヘッダーのカスタマイズ

HTTP ヘッダーをカスタマイズするには、次の 2 つのオプションがあります。

  • addHeadersConverter() を呼び出して追加のヘッダーを追加する

  • setHeadersConverter() を呼び出してヘッダーを完全にカスタマイズする

addHeadersConverter() を使用すると、すべてのリクエストに追加されるデフォルトのヘッダーに影響を与えずに、追加のヘッダーを含めることができます。次の例では、registrationId が spring の場合にリクエストに User-Agent ヘッダーを追加します。

追加の HTTP ヘッダーを含める
  • Java

  • Kotlin

RestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =
	new RestClientAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.addHeadersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	HttpHeaders headers = new HttpHeaders();
	if (clientRegistration.getRegistrationId().equals("spring")) {
		headers.set(HttpHeaders.USER_AGENT, "my-user-agent");
	}
	return headers;
});
val accessTokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()
accessTokenResponseClient.addHeadersConverter { grantRequest ->
	val clientRegistration = grantRequest.getClientRegistration()
	val headers = HttpHeaders()
	if (clientRegistration.getRegistrationId() == "spring") {
        headers[HttpHeaders.USER_AGENT] = "my-user-agent"
	}
	headers
}

DefaultOAuth2TokenRequestHeadersConverter を再利用するか、setHeadersConverter() を使用してカスタム実装を提供することで、ヘッダーを完全にカスタマイズできます。次の例では、DefaultOAuth2TokenRequestHeadersConverter を再利用し、encodeClientCredentials を無効にして、HTTP 基本認証情報が application/x-www-form-urlencoded でエンコードされないようにしています。

HTTP ヘッダーをカスタマイズする
  • Java

  • Kotlin

DefaultOAuth2TokenRequestHeadersConverter headersConverter =
	new DefaultOAuth2TokenRequestHeadersConverter();
headersConverter.setEncodeClientCredentials(false);

RestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =
	new RestClientAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setHeadersConverter(headersConverter);
val headersConverter = DefaultOAuth2TokenRequestHeadersConverter()
headersConverter.setEncodeClientCredentials(false)

val accessTokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()
accessTokenResponseClient.setHeadersConverter(headersConverter)

リクエストパラメーターのカスタマイズ

リクエストパラメーターをカスタマイズするには、次の 3 つのオプションがあります。

  • addParametersConverter() を呼び出して追加のパラメーターを追加する

  • setParametersConverter() を呼び出してパラメーターを上書きする

  • setParametersCustomizer() を呼び出してパラメーターを完全にカスタマイズする

setParametersConverter() を使用すると、ユーザーがすべてのデフォルトパラメーターを自分で指定する必要があるため、パラメーターを完全にカスタマイズすることはできません。デフォルトパラメーターは常に提供されますが、setParametersCustomizer() を呼び出すことで完全にカスタマイズしたり省略したりできます。

addParametersConverter() を使用すると、すべてのリクエストに追加されるデフォルトのパラメーターに影響を与えずに、追加のパラメーターを含めることができます。次の例では、registrationId が keycloak の場合に、リクエストに audience パラメーターを追加します。

追加のリクエストパラメーターを含める
  • Java

  • Kotlin

RestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =
	new RestClientAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.addParametersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
	if (clientRegistration.getRegistrationId().equals("keycloak")) {
		parameters.set(OAuth2ParameterNames.AUDIENCE, "my-audience");
	}
	return parameters;
});
val accessTokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()
accessTokenResponseClient.addParametersConverter { grantRequest ->
	val clientRegistration = grantRequest.getClientRegistration()
	val parameters = LinkedMultiValueMap<String, String>()
	if (clientRegistration.getRegistrationId() == "keycloak") {
        parameters[OAuth2ParameterNames.AUDIENCE] = "my-audience"
	}
	parameters
}

setParametersConverter() を使用してデフォルトのパラメーターを上書きできます。次の例では、registrationId が okta の場合に client_id パラメーターを上書きします。

リクエストパラメーターの上書き
  • Java

  • Kotlin

RestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =
	new RestClientAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setParametersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	LinkedMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
	if (clientRegistration.getRegistrationId().equals("okta")) {
		parameters.set(OAuth2ParameterNames.CLIENT_ID, "my-client");
	}
	return parameters;
});
val parametersConverter = DefaultOAuth2TokenRequestParametersConverter<OAuth2AuthorizationCodeGrantRequest>()
parametersConverter.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

val accessTokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()
accessTokenResponseClient.setParametersConverter { grantRequest ->
    val clientRegistration = grantRequest.getClientRegistration()
	val parameters = LinkedMultiValueMap<String, String>()
	if (clientRegistration.getRegistrationId() == "okta") {
        parameters[OAuth2ParameterNames.CLIENT_ID] = "my-client"
	}
	parameters
}

setParametersCustomizer() を使用すると、パラメーターを完全にカスタマイズできます (デフォルトパラメーターの省略を含む)。次の例では、リクエストに client_assertion パラメーターが存在する場合に client_id パラメーターを省略します。

リクエストパラメーターを省略する
  • Java

  • Kotlin

RestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =
	new RestClientAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setParametersCustomizer(parameters -> {
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID);
	}
});
val accessTokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()
accessTokenResponseClient.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

アクセストークンレスポンスのカスタマイズ

RestClientAuthorizationCodeTokenResponseClient は、OAuth 2.0 アクセストークンレスポンスのレスポンスパラメーターとエラー処理をカスタマイズするためのフックを提供します。

RestClient のカスタマイズ

事前設定された RestClient を setRestClient() に提供することで、トークンレスポンスをカスタマイズできます。デフォルトの RestClient は次のように設定されています。

デフォルトの RestClient 構成
  • Java

  • Kotlin

RestClient restClient = RestClient.builder()
	.messageConverters(messageConverters -> {
		messageConverters.clear();
		messageConverters.add(new FormHttpMessageConverter());
		messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());
	})
	.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())
	.build();

RestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =
	new RestClientAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setRestClient(restClient);
val restClient = RestClient.builder()
	.messageConverters { messageConverters ->
		messageConverters.clear()
		messageConverters.add(FormHttpMessageConverter())
		messageConverters.add(OAuth2AccessTokenResponseHttpMessageConverter())
	}
	.defaultStatusHandler(OAuth2ErrorResponseErrorHandler())
	.build()

val accessTokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()
accessTokenResponseClient.setRestClient(restClient)

OAuth2AccessTokenResponseHttpMessageConverter は、OAuth 2.0 アクセストークンレスポンスの HttpMessageConverter です。setAccessTokenResponseConverter() を呼び出すことで、トークンレスポンスパラメーターの OAuth2AccessTokenResponse への変換をカスタマイズできます。デフォルトの実装は DefaultMapOAuth2AccessTokenResponseConverter です。

OAuth2ErrorResponseErrorHandler は、400 Bad Request などの OAuth 2.0 エラーを処理できる ResponseErrorHandler です。OAuth2ErrorHttpMessageConverter を使用して、OAuth 2.0 エラーパラメーターを OAuth2Error に変換します。setErrorConverter() を呼び出すことで、トークンレスポンスパラメーターの OAuth2Error への変換をカスタマイズできます。

Spring MVC FormHttpMessageConverter は、OAuth 2.0 アクセストークンリクエストを送信するときに使用されるため必須です。

レスポンスパラメーターのカスタマイズ

次の例は、トークンレスポンスパラメーターを OAuth2AccessTokenResponse に変換することをカスタマイズするための出発点を示しています。

アクセストークンレスポンスコンバーターをカスタマイズする
  • Java

  • Kotlin

OAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseMessageConverter =
	new OAuth2AccessTokenResponseHttpMessageConverter();
accessTokenResponseMessageConverter.setAccessTokenResponseConverter(parameters -> {
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build();
});
val accessTokenResponseMessageConverter = OAuth2AccessTokenResponseHttpMessageConverter()
accessTokenResponseMessageConverter.setAccessTokenResponseConverter { parameters ->
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build()
}

エラー処理のカスタマイズ

次の例は、Error パラメーターから OAuth2Error への変換をカスタマイズするための出発点を示しています。

アクセストークンエラーハンドラーをカスタマイズする
  • Java

  • Kotlin

OAuth2ErrorHttpMessageConverter errorConverter =
	new OAuth2ErrorHttpMessageConverter();
errorConverter.setErrorConverter(parameters -> {
	// ...
	return new OAuth2Error("custom-error", "custom description", "custom-uri");
});

OAuth2ErrorResponseErrorHandler errorHandler =
	new OAuth2ErrorResponseErrorHandler();
errorHandler.setErrorConverter(errorConverter);
val errorConverter = OAuth2ErrorHttpMessageConverter()
errorConverter.setErrorConverter { parameters ->
	// ...
	return OAuth2Error("custom-error", "custom description", "custom-uri")
}

val errorHandler = OAuth2ErrorResponseErrorHandler()
errorHandler.setErrorConverter(errorConverter)

DSL を使用してカスタマイズする

RestClientAuthorizationCodeTokenResponseClient をカスタマイズする場合でも、独自の OAuth2AccessTokenResponseClient 実装を提供する場合でも、次のように DSL を使用して構成できます ( Bean を公開する代わりに)。

DSL 経由のアクセストークンレスポンス構成
  • Java

  • Kotlin

  • XML

@Configuration
@EnableWebSecurity
public class OAuth2ClientSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Client((oauth2) -> oauth2
				.authorizationCodeGrant((codeGrant) -> codeGrant
					.accessTokenResponseClient(this.accessTokenResponseClient())
					// ...
				)
			);
		return http.build();
	}
}
@Configuration
@EnableWebSecurity
class OAuth2ClientSecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Client {
                authorizationCodeGrant {
                    accessTokenResponseClient = accessTokenResponseClient()
                }
            }
        }
        return http.build()
    }
}
<http>
	<oauth2-client>
		<authorization-code-grant access-token-response-client-ref="accessTokenResponseClient"/>
	</oauth2-client>
</http>

リフレッシュトークン

リフレッシュトークン [IETF] (英語) の詳細については、OAuth 2.0 認証フレームワークを参照してください。

アクセストークンのリフレッシュ

リフレッシュトークンの付与については、アクセストークンリクエスト / レスポンス [IETF] (英語) プロトコルフローを参照してください。

リフレッシュトークン付与の OAuth2AccessTokenResponseClient のデフォルト実装は RestClientRefreshTokenTokenResponseClient です。これは、RestClient インスタンスを使用して、認可サーバーのトークンエンドポイントでアクセストークンを取得します。

RestClientRefreshTokenTokenResponseClient は非常に柔軟性が高く、リフレッシュトークン付与に対する OAuth 2.0 アクセストークンのリクエストとレスポンスをカスタマイズするためのオプションをいくつか提供します。詳細については、次のユースケースから選択してください。

アクセストークンリクエストのカスタマイズ

RestClientRefreshTokenTokenResponseClient は、OAuth 2.0 アクセストークンリクエストの HTTP ヘッダーとリクエストパラメーターをカスタマイズするためのフックを提供します。

リクエストヘッダーのカスタマイズ

HTTP ヘッダーをカスタマイズするには、次の 2 つのオプションがあります。

  • addHeadersConverter() を呼び出して追加のヘッダーを追加する

  • setHeadersConverter() を呼び出してヘッダーを完全にカスタマイズする

addHeadersConverter() を使用すると、すべてのリクエストに追加されるデフォルトのヘッダーに影響を与えずに、追加のヘッダーを含めることができます。次の例では、registrationId が spring の場合にリクエストに User-Agent ヘッダーを追加します。

追加の HTTP ヘッダーを含める
  • Java

  • Kotlin

RestClientRefreshTokenTokenResponseClient accessTokenResponseClient =
	new RestClientRefreshTokenTokenResponseClient();
accessTokenResponseClient.addHeadersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	HttpHeaders headers = new HttpHeaders();
	if (clientRegistration.getRegistrationId().equals("spring")) {
		headers.set(HttpHeaders.USER_AGENT, "my-user-agent");
	}
	return headers;
});
val accessTokenResponseClient = RestClientRefreshTokenTokenResponseClient()
accessTokenResponseClient.addHeadersConverter { grantRequest ->
	val clientRegistration = grantRequest.getClientRegistration()
	val headers = HttpHeaders()
	if (clientRegistration.getRegistrationId() == "spring") {
        headers[HttpHeaders.USER_AGENT] = "my-user-agent"
	}
	headers
}

DefaultOAuth2TokenRequestHeadersConverter を再利用するか、setHeadersConverter() を使用してカスタム実装を提供することで、ヘッダーを完全にカスタマイズできます。次の例では、DefaultOAuth2TokenRequestHeadersConverter を再利用し、encodeClientCredentials を無効にして、HTTP 基本認証情報が application/x-www-form-urlencoded でエンコードされないようにしています。

HTTP ヘッダーをカスタマイズする
  • Java

  • Kotlin

DefaultOAuth2TokenRequestHeadersConverter headersConverter =
	new DefaultOAuth2TokenRequestHeadersConverter();
headersConverter.setEncodeClientCredentials(false);

RestClientRefreshTokenTokenResponseClient accessTokenResponseClient =
	new RestClientRefreshTokenTokenResponseClient();
accessTokenResponseClient.setHeadersConverter(headersConverter);
val headersConverter = DefaultOAuth2TokenRequestHeadersConverter()
headersConverter.setEncodeClientCredentials(false)

val accessTokenResponseClient = RestClientRefreshTokenTokenResponseClient()
accessTokenResponseClient.setHeadersConverter(headersConverter)

リクエストパラメーターのカスタマイズ

リクエストパラメーターをカスタマイズするには、次の 3 つのオプションがあります。

  • addParametersConverter() を呼び出して追加のパラメーターを追加する

  • setParametersConverter() を呼び出してパラメーターを上書きする

  • setParametersCustomizer() を呼び出してパラメーターを完全にカスタマイズする

setParametersConverter() を使用すると、ユーザーがすべてのデフォルトパラメーターを自分で指定する必要があるため、パラメーターを完全にカスタマイズすることはできません。デフォルトパラメーターは常に提供されますが、setParametersCustomizer() を呼び出すことで完全にカスタマイズしたり省略したりできます。

addParametersConverter() を使用すると、すべてのリクエストに追加されるデフォルトのパラメーターに影響を与えずに、追加のパラメーターを含めることができます。次の例では、registrationId が keycloak の場合に、リクエストに audience パラメーターを追加します。

追加のリクエストパラメーターを含める
  • Java

  • Kotlin

RestClientRefreshTokenTokenResponseClient accessTokenResponseClient =
	new RestClientRefreshTokenTokenResponseClient();
accessTokenResponseClient.addParametersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
	if (clientRegistration.getRegistrationId().equals("keycloak")) {
		parameters.set(OAuth2ParameterNames.AUDIENCE, "my-audience");
	}
	return parameters;
});
val accessTokenResponseClient = RestClientRefreshTokenTokenResponseClient()
accessTokenResponseClient.addParametersConverter { grantRequest ->
	val clientRegistration = grantRequest.getClientRegistration()
	val parameters = LinkedMultiValueMap<String, String>()
	if (clientRegistration.getRegistrationId() == "keycloak") {
        parameters[OAuth2ParameterNames.AUDIENCE] = "my-audience"
	}
	parameters
}

setParametersConverter() を使用してデフォルトのパラメーターを上書きできます。次の例では、registrationId が okta の場合に client_id パラメーターを上書きします。

リクエストパラメーターの上書き
  • Java

  • Kotlin

RestClientRefreshTokenTokenResponseClient accessTokenResponseClient =
	new RestClientRefreshTokenTokenResponseClient();
accessTokenResponseClient.setParametersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	LinkedMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
	if (clientRegistration.getRegistrationId().equals("okta")) {
		parameters.set(OAuth2ParameterNames.CLIENT_ID, "my-client");
	}
	return parameters;
});
val parametersConverter = DefaultOAuth2TokenRequestParametersConverter<OAuth2RefreshTokenGrantRequest>()
parametersConverter.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

val accessTokenResponseClient = RestClientRefreshTokenTokenResponseClient()
accessTokenResponseClient.setParametersConverter { grantRequest ->
    val clientRegistration = grantRequest.getClientRegistration()
	val parameters = LinkedMultiValueMap<String, String>()
	if (clientRegistration.getRegistrationId() == "okta") {
        parameters[OAuth2ParameterNames.CLIENT_ID] = "my-client"
	}
	parameters
}

setParametersCustomizer() を使用すると、パラメーターを完全にカスタマイズできます (デフォルトパラメーターの省略を含む)。次の例では、リクエストに client_assertion パラメーターが存在する場合に client_id パラメーターを省略します。

リクエストパラメーターを省略する
  • Java

  • Kotlin

RestClientRefreshTokenTokenResponseClient accessTokenResponseClient =
	new RestClientRefreshTokenTokenResponseClient();
accessTokenResponseClient.setParametersCustomizer(parameters -> {
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID);
	}
});
val accessTokenResponseClient = RestClientRefreshTokenTokenResponseClient()
accessTokenResponseClient.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

アクセストークンレスポンスのカスタマイズ

RestClientRefreshTokenTokenResponseClient は、OAuth 2.0 アクセストークンレスポンスのレスポンスパラメーターとエラー処理をカスタマイズするためのフックを提供します。

RestClient のカスタマイズ

事前設定された RestClient を setRestClient() に提供することで、トークンレスポンスをカスタマイズできます。デフォルトの RestClient は次のように設定されています。

デフォルトの RestClient 構成
  • Java

  • Kotlin

RestClient restClient = RestClient.builder()
	.messageConverters(messageConverters -> {
		messageConverters.clear();
		messageConverters.add(new FormHttpMessageConverter());
		messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());
	})
	.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())
	.build();

RestClientRefreshTokenTokenResponseClient accessTokenResponseClient =
	new RestClientRefreshTokenTokenResponseClient();
accessTokenResponseClient.setRestClient(restClient);
val restClient = RestClient.builder()
	.messageConverters { messageConverters ->
		messageConverters.clear()
		messageConverters.add(FormHttpMessageConverter())
		messageConverters.add(OAuth2AccessTokenResponseHttpMessageConverter())
	}
	.defaultStatusHandler(OAuth2ErrorResponseErrorHandler())
	.build()

val accessTokenResponseClient = RestClientRefreshTokenTokenResponseClient()
accessTokenResponseClient.setRestClient(restClient)

OAuth2AccessTokenResponseHttpMessageConverter は、OAuth 2.0 アクセストークンレスポンスの HttpMessageConverter です。setAccessTokenResponseConverter() を呼び出すことで、トークンレスポンスパラメーターの OAuth2AccessTokenResponse への変換をカスタマイズできます。デフォルトの実装は DefaultMapOAuth2AccessTokenResponseConverter です。

OAuth2ErrorResponseErrorHandler は、400 Bad Request などの OAuth 2.0 エラーを処理できる ResponseErrorHandler です。OAuth2ErrorHttpMessageConverter を使用して、OAuth 2.0 エラーパラメーターを OAuth2Error に変換します。setErrorConverter() を呼び出すことで、トークンレスポンスパラメーターの OAuth2Error への変換をカスタマイズできます。

Spring MVC FormHttpMessageConverter は、OAuth 2.0 アクセストークンリクエストを送信するときに使用されるため必須です。

レスポンスパラメーターのカスタマイズ

次の例は、トークンレスポンスパラメーターを OAuth2AccessTokenResponse に変換することをカスタマイズするための出発点を示しています。

アクセストークンレスポンスコンバーターをカスタマイズする
  • Java

  • Kotlin

OAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseMessageConverter =
	new OAuth2AccessTokenResponseHttpMessageConverter();
accessTokenResponseMessageConverter.setAccessTokenResponseConverter(parameters -> {
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build();
});
val accessTokenResponseMessageConverter = OAuth2AccessTokenResponseHttpMessageConverter()
accessTokenResponseMessageConverter.setAccessTokenResponseConverter { parameters ->
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build()
}

エラー処理のカスタマイズ

次の例は、Error パラメーターから OAuth2Error への変換をカスタマイズするための出発点を示しています。

アクセストークンエラーハンドラーをカスタマイズする
  • Java

  • Kotlin

OAuth2ErrorHttpMessageConverter errorConverter =
	new OAuth2ErrorHttpMessageConverter();
errorConverter.setErrorConverter(parameters -> {
	// ...
	return new OAuth2Error("custom-error", "custom description", "custom-uri");
});

OAuth2ErrorResponseErrorHandler errorHandler =
	new OAuth2ErrorResponseErrorHandler();
errorHandler.setErrorConverter(errorConverter);
val errorConverter = OAuth2ErrorHttpMessageConverter()
errorConverter.setErrorConverter { parameters ->
	// ...
	return OAuth2Error("custom-error", "custom description", "custom-uri")
}

val errorHandler = OAuth2ErrorResponseErrorHandler()
errorHandler.setErrorConverter(errorConverter)

ビルダーを使用してカスタマイズする

RestClientRefreshTokenTokenResponseClient をカスタマイズするか、独自の OAuth2AccessTokenResponseClient 実装を提供するかにかかわらず、次のように OAuth2AuthorizedClientProviderBuilder を使用して構成できます ( Bean を公開する代わりに)。

ビルダーによるアクセストークンレスポンスの構成
  • Java

  • Kotlin

// Customize
OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient = ...

OAuth2AuthorizedClientProvider authorizedClientProvider =
		OAuth2AuthorizedClientProviderBuilder.builder()
				.authorizationCode()
				.refreshToken((configurer) -> configurer.accessTokenResponseClient(refreshTokenTokenResponseClient))
				.build();

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val refreshTokenTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> = ...

val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
        .authorizationCode()
        .refreshToken { it.accessTokenResponseClient(refreshTokenTokenResponseClient) }
        .build()

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

OAuth2AuthorizedClientProviderBuilder.builder().refreshToken() は RefreshTokenOAuth2AuthorizedClientProvider を構成します。これは、リフレッシュトークン許可用の OAuth2AuthorizedClientProvider の実装です。

OAuth2RefreshToken は、authorization_code 付与型のアクセストークンレスポンスでオプションとして返されます。OAuth2AuthorizedClient.getRefreshToken() が利用可能で、OAuth2AuthorizedClient.getAccessToken() が期限切れの場合、RefreshTokenOAuth2AuthorizedClientProvider によって自動的にリフレッシュされます。

クライアント資格情報

クライアント資格情報 [IETF] (英語) 付与の詳細については、OAuth 2.0 認可フレームワークを参照してください。

アクセストークンのリクエスト

クライアント資格情報の付与については、アクセストークンリクエスト / レスポンス [IETF] (英語) プロトコルフローを参照してください。

クライアント資格情報付与の OAuth2AccessTokenResponseClient のデフォルト実装は RestClientClientCredentialsTokenResponseClient です。これは、RestClient インスタンスを使用して、認可サーバーのトークンエンドポイントでアクセストークンを取得します。

RestClientClientCredentialsTokenResponseClient は非常に柔軟性が高く、クライアント資格情報の付与に対する OAuth 2.0 アクセストークンのリクエストとレスポンスをカスタマイズするためのオプションをいくつか提供します。詳細については、次のユースケースから選択してください。

アクセストークンリクエストのカスタマイズ

RestClientClientCredentialsTokenResponseClient は、OAuth 2.0 アクセストークンリクエストの HTTP ヘッダーとリクエストパラメーターをカスタマイズするためのフックを提供します。

リクエストヘッダーのカスタマイズ

HTTP ヘッダーをカスタマイズするには、次の 2 つのオプションがあります。

  • addHeadersConverter() を呼び出して追加のヘッダーを追加する

  • setHeadersConverter() を呼び出してヘッダーを完全にカスタマイズする

addHeadersConverter() を使用すると、すべてのリクエストに追加されるデフォルトのヘッダーに影響を与えずに、追加のヘッダーを含めることができます。次の例では、registrationId が spring の場合にリクエストに User-Agent ヘッダーを追加します。

追加の HTTP ヘッダーを含める
  • Java

  • Kotlin

RestClientClientCredentialsTokenResponseClient accessTokenResponseClient =
	new RestClientClientCredentialsTokenResponseClient();
accessTokenResponseClient.addHeadersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	HttpHeaders headers = new HttpHeaders();
	if (clientRegistration.getRegistrationId().equals("spring")) {
		headers.set(HttpHeaders.USER_AGENT, "my-user-agent");
	}
	return headers;
});
val accessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()
accessTokenResponseClient.addHeadersConverter { grantRequest ->
	val clientRegistration = grantRequest.getClientRegistration()
	val headers = HttpHeaders()
	if (clientRegistration.getRegistrationId() == "spring") {
        headers[HttpHeaders.USER_AGENT] = "my-user-agent"
	}
	headers
}

DefaultOAuth2TokenRequestHeadersConverter を再利用するか、setHeadersConverter() を使用してカスタム実装を提供することで、ヘッダーを完全にカスタマイズできます。次の例では、DefaultOAuth2TokenRequestHeadersConverter を再利用し、encodeClientCredentials を無効にして、HTTP 基本認証情報が application/x-www-form-urlencoded でエンコードされないようにしています。

HTTP ヘッダーをカスタマイズする
  • Java

  • Kotlin

DefaultOAuth2TokenRequestHeadersConverter headersConverter =
	new DefaultOAuth2TokenRequestHeadersConverter();
headersConverter.setEncodeClientCredentials(false);

RestClientClientCredentialsTokenResponseClient accessTokenResponseClient =
	new RestClientClientCredentialsTokenResponseClient();
accessTokenResponseClient.setHeadersConverter(headersConverter);
val headersConverter = DefaultOAuth2TokenRequestHeadersConverter()
headersConverter.setEncodeClientCredentials(false)

val accessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()
accessTokenResponseClient.setHeadersConverter(headersConverter)

リクエストパラメーターのカスタマイズ

リクエストパラメーターをカスタマイズするには、次の 3 つのオプションがあります。

  • addParametersConverter() を呼び出して追加のパラメーターを追加する

  • setParametersConverter() を呼び出してパラメーターを上書きする

  • setParametersCustomizer() を呼び出してパラメーターを完全にカスタマイズする

setParametersConverter() を使用すると、ユーザーがすべてのデフォルトパラメーターを自分で指定する必要があるため、パラメーターを完全にカスタマイズすることはできません。デフォルトパラメーターは常に提供されますが、setParametersCustomizer() を呼び出すことで完全にカスタマイズしたり省略したりできます。

addParametersConverter() を使用すると、すべてのリクエストに追加されるデフォルトのパラメーターに影響を与えずに、追加のパラメーターを含めることができます。次の例では、registrationId が keycloak の場合に、リクエストに audience パラメーターを追加します。

追加のリクエストパラメーターを含める
  • Java

  • Kotlin

RestClientClientCredentialsTokenResponseClient accessTokenResponseClient =
	new RestClientClientCredentialsTokenResponseClient();
accessTokenResponseClient.addParametersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
	if (clientRegistration.getRegistrationId().equals("keycloak")) {
		parameters.set(OAuth2ParameterNames.AUDIENCE, "my-audience");
	}
	return parameters;
});
val accessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()
accessTokenResponseClient.addParametersConverter { grantRequest ->
	val clientRegistration = grantRequest.getClientRegistration()
	val parameters = LinkedMultiValueMap<String, String>()
	if (clientRegistration.getRegistrationId() == "keycloak") {
        parameters[OAuth2ParameterNames.AUDIENCE] = "my-audience"
	}
	parameters
}

setParametersConverter() を使用してデフォルトのパラメーターを上書きできます。次の例では、registrationId が okta の場合に client_id パラメーターを上書きします。

リクエストパラメーターの上書き
  • Java

  • Kotlin

RestClientClientCredentialsTokenResponseClient accessTokenResponseClient =
	new RestClientClientCredentialsTokenResponseClient();
accessTokenResponseClient.setParametersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	LinkedMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
	if (clientRegistration.getRegistrationId().equals("okta")) {
		parameters.set(OAuth2ParameterNames.CLIENT_ID, "my-client");
	}
	return parameters;
});
val parametersConverter = DefaultOAuth2TokenRequestParametersConverter<OAuth2ClientCredentialsGrantRequest>()
parametersConverter.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

val accessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()
accessTokenResponseClient.setParametersConverter { grantRequest ->
    val clientRegistration = grantRequest.getClientRegistration()
	val parameters = LinkedMultiValueMap<String, String>()
	if (clientRegistration.getRegistrationId() == "okta") {
        parameters[OAuth2ParameterNames.CLIENT_ID] = "my-client"
	}
	parameters
}

setParametersCustomizer() を使用すると、パラメーターを完全にカスタマイズできます (デフォルトパラメーターの省略を含む)。次の例では、リクエストに client_assertion パラメーターが存在する場合に client_id パラメーターを省略します。

リクエストパラメーターを省略する
  • Java

  • Kotlin

RestClientClientCredentialsTokenResponseClient accessTokenResponseClient =
	new RestClientClientCredentialsTokenResponseClient();
accessTokenResponseClient.setParametersCustomizer(parameters -> {
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID);
	}
});
val accessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()
accessTokenResponseClient.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

アクセストークンレスポンスのカスタマイズ

RestClientClientCredentialsTokenResponseClient は、OAuth 2.0 アクセストークンレスポンスのレスポンスパラメーターとエラー処理をカスタマイズするためのフックを提供します。

RestClient のカスタマイズ

事前設定された RestClient を setRestClient() に提供することで、トークンレスポンスをカスタマイズできます。デフォルトの RestClient は次のように設定されています。

デフォルトの RestClient 構成
  • Java

  • Kotlin

RestClient restClient = RestClient.builder()
	.messageConverters(messageConverters -> {
		messageConverters.clear();
		messageConverters.add(new FormHttpMessageConverter());
		messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());
	})
	.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())
	.build();

RestClientClientCredentialsTokenResponseClient accessTokenResponseClient =
	new RestClientClientCredentialsTokenResponseClient();
accessTokenResponseClient.setRestClient(restClient);
val restClient = RestClient.builder()
	.messageConverters { messageConverters ->
		messageConverters.clear()
		messageConverters.add(FormHttpMessageConverter())
		messageConverters.add(OAuth2AccessTokenResponseHttpMessageConverter())
	}
	.defaultStatusHandler(OAuth2ErrorResponseErrorHandler())
	.build()

val accessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()
accessTokenResponseClient.setRestClient(restClient)

OAuth2AccessTokenResponseHttpMessageConverter は、OAuth 2.0 アクセストークンレスポンスの HttpMessageConverter です。setAccessTokenResponseConverter() を呼び出すことで、トークンレスポンスパラメーターの OAuth2AccessTokenResponse への変換をカスタマイズできます。デフォルトの実装は DefaultMapOAuth2AccessTokenResponseConverter です。

OAuth2ErrorResponseErrorHandler は、400 Bad Request などの OAuth 2.0 エラーを処理できる ResponseErrorHandler です。OAuth2ErrorHttpMessageConverter を使用して、OAuth 2.0 エラーパラメーターを OAuth2Error に変換します。setErrorConverter() を呼び出すことで、トークンレスポンスパラメーターの OAuth2Error への変換をカスタマイズできます。

Spring MVC FormHttpMessageConverter は、OAuth 2.0 アクセストークンリクエストを送信するときに使用されるため必須です。

レスポンスパラメーターのカスタマイズ

次の例は、トークンレスポンスパラメーターを OAuth2AccessTokenResponse に変換することをカスタマイズするための出発点を示しています。

アクセストークンレスポンスコンバーターをカスタマイズする
  • Java

  • Kotlin

OAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseMessageConverter =
	new OAuth2AccessTokenResponseHttpMessageConverter();
accessTokenResponseMessageConverter.setAccessTokenResponseConverter(parameters -> {
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build();
});
val accessTokenResponseMessageConverter = OAuth2AccessTokenResponseHttpMessageConverter()
accessTokenResponseMessageConverter.setAccessTokenResponseConverter { parameters ->
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build()
}

エラー処理のカスタマイズ

次の例は、Error パラメーターから OAuth2Error への変換をカスタマイズするための出発点を示しています。

アクセストークンエラーハンドラーをカスタマイズする
  • Java

  • Kotlin

OAuth2ErrorHttpMessageConverter errorConverter =
	new OAuth2ErrorHttpMessageConverter();
errorConverter.setErrorConverter(parameters -> {
	// ...
	return new OAuth2Error("custom-error", "custom description", "custom-uri");
});

OAuth2ErrorResponseErrorHandler errorHandler =
	new OAuth2ErrorResponseErrorHandler();
errorHandler.setErrorConverter(errorConverter);
val errorConverter = OAuth2ErrorHttpMessageConverter()
errorConverter.setErrorConverter { parameters ->
	// ...
	return OAuth2Error("custom-error", "custom description", "custom-uri")
}

val errorHandler = OAuth2ErrorResponseErrorHandler()
errorHandler.setErrorConverter(errorConverter)

ビルダーを使用してカスタマイズする

RestClientClientCredentialsTokenResponseClient をカスタマイズするか、独自の OAuth2AccessTokenResponseClient 実装を提供するかにかかわらず、次のように OAuth2AuthorizedClientProviderBuilder を使用して構成できます ( Bean を公開する代わりに)。

ビルダーによるアクセストークンレスポンスの構成
  • Java

  • Kotlin

// Customize
OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient = ...

OAuth2AuthorizedClientProvider authorizedClientProvider =
		OAuth2AuthorizedClientProviderBuilder.builder()
				.clientCredentials((configurer) -> configurer.accessTokenResponseClient(clientCredentialsTokenResponseClient))
				.build();

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val clientCredentialsTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> = ...

val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
        .clientCredentials { it.accessTokenResponseClient(clientCredentialsTokenResponseClient) }
        .build()

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

OAuth2AuthorizedClientProviderBuilder.builder().clientCredentials() は、ClientCredentialsOAuth2AuthorizedClientProvider を構成します。これは、Client Credentials 付与のための OAuth2AuthorizedClientProvider の実装です。

アクセストークンの使用

OAuth 2.0 クライアント登録の次の Spring Boot プロパティを検討してください。

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: client_credentials
            scope: read, write
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

さらに、次の OAuth2AuthorizedClientManager @Bean を検討してください。

  • Java

  • Kotlin

@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
		ClientRegistrationRepository clientRegistrationRepository,
		OAuth2AuthorizedClientRepository authorizedClientRepository) {

	OAuth2AuthorizedClientProvider authorizedClientProvider =
			OAuth2AuthorizedClientProviderBuilder.builder()
					.clientCredentials()
					.build();

	DefaultOAuth2AuthorizedClientManager authorizedClientManager =
			new DefaultOAuth2AuthorizedClientManager(
					clientRegistrationRepository, authorizedClientRepository);
	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

	return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ClientRegistrationRepository,
        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .clientCredentials()
            .build()
    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
    return authorizedClientManager
}

上記のプロパティと Bean を指定すると、次のように OAuth2AccessToken を取得できます。

  • Java

  • Kotlin

@Controller
public class OAuth2ClientController {

	@Autowired
	private OAuth2AuthorizedClientManager authorizedClientManager;

	@GetMapping("/")
	public String index(Authentication authentication,
						HttpServletRequest servletRequest,
						HttpServletResponse servletResponse) {

		OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
				.principal(authentication)
				.attributes(attrs -> {
					attrs.put(HttpServletRequest.class.getName(), servletRequest);
					attrs.put(HttpServletResponse.class.getName(), servletResponse);
				})
				.build();
		OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);

		OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

		// ...

		return "index";
	}
}
class OAuth2ClientController {

    @Autowired
    private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager

    @GetMapping("/")
    fun index(authentication: Authentication?,
              servletRequest: HttpServletRequest,
              servletResponse: HttpServletResponse): String {
        val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(authentication)
                .attributes(Consumer { attrs: MutableMap<String, Any> ->
                    attrs[HttpServletRequest::class.java.name] = servletRequest
                    attrs[HttpServletResponse::class.java.name] = servletResponse
                })
                .build()
        val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
        val accessToken: OAuth2AccessToken = authorizedClient.accessToken

        // ...

        return "index"
    }
}

HttpServletRequest と HttpServletResponse はどちらもオプションの属性です。指定しない場合、RequestContextHolder.getRequestAttributes() を使用してデフォルトで ServletRequestAttributes になります。

JWT ベアラー

JWT ベアラー [IETF] (英語) 付与の詳細については、OAuth 2.0 クライアント認証および認可付与の JSON Web トークン(JWT)プロファイルを参照してください。

アクセストークンのリクエスト

JWT Bearer Grant については、アクセストークンリクエスト / レスポンス [IETF] (英語) プロトコルフローを参照してください。

JWT ベアラー付与の OAuth2AccessTokenResponseClient のデフォルト実装は RestClientJwtBearerTokenResponseClient です。これは、RestClient インスタンスを使用して、認可サーバーのトークンエンドポイントでアクセストークンを取得します。

RestClientJwtBearerTokenResponseClient は非常に柔軟性が高く、JWT ベアラー付与の OAuth 2.0 アクセストークンリクエストとレスポンスをカスタマイズするためのオプションをいくつか提供します。詳細については、次のユースケースから選択してください。

アクセストークンリクエストのカスタマイズ

RestClientJwtBearerTokenResponseClient は、OAuth 2.0 アクセストークンリクエストの HTTP ヘッダーとリクエストパラメーターをカスタマイズするためのフックを提供します。

リクエストヘッダーのカスタマイズ

HTTP ヘッダーをカスタマイズするには、次の 2 つのオプションがあります。

  • addHeadersConverter() を呼び出して追加のヘッダーを追加する

  • setHeadersConverter() を呼び出してヘッダーを完全にカスタマイズする

addHeadersConverter() を使用すると、すべてのリクエストに追加されるデフォルトのヘッダーに影響を与えずに、追加のヘッダーを含めることができます。次の例では、registrationId が spring の場合にリクエストに User-Agent ヘッダーを追加します。

追加の HTTP ヘッダーを含める
  • Java

  • Kotlin

RestClientJwtBearerTokenResponseClient accessTokenResponseClient =
	new RestClientJwtBearerTokenResponseClient();
accessTokenResponseClient.addHeadersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	HttpHeaders headers = new HttpHeaders();
	if (clientRegistration.getRegistrationId().equals("spring")) {
		headers.set(HttpHeaders.USER_AGENT, "my-user-agent");
	}
	return headers;
});
val accessTokenResponseClient = RestClientJwtBearerTokenResponseClient()
accessTokenResponseClient.addHeadersConverter { grantRequest ->
	val clientRegistration = grantRequest.getClientRegistration()
	val headers = HttpHeaders()
	if (clientRegistration.getRegistrationId() == "spring") {
        headers[HttpHeaders.USER_AGENT] = "my-user-agent"
	}
	headers
}

DefaultOAuth2TokenRequestHeadersConverter を再利用するか、setHeadersConverter() を使用してカスタム実装を提供することで、ヘッダーを完全にカスタマイズできます。次の例では、DefaultOAuth2TokenRequestHeadersConverter を再利用し、encodeClientCredentials を無効にして、HTTP 基本認証情報が application/x-www-form-urlencoded でエンコードされないようにしています。

HTTP ヘッダーをカスタマイズする
  • Java

  • Kotlin

DefaultOAuth2TokenRequestHeadersConverter headersConverter =
	new DefaultOAuth2TokenRequestHeadersConverter();
headersConverter.setEncodeClientCredentials(false);

RestClientJwtBearerTokenResponseClient accessTokenResponseClient =
	new RestClientJwtBearerTokenResponseClient();
accessTokenResponseClient.setHeadersConverter(headersConverter);
val headersConverter = DefaultOAuth2TokenRequestHeadersConverter()
headersConverter.setEncodeClientCredentials(false)

val accessTokenResponseClient = RestClientJwtBearerTokenResponseClient()
accessTokenResponseClient.setHeadersConverter(headersConverter)

リクエストパラメーターのカスタマイズ

リクエストパラメーターをカスタマイズするには、次の 3 つのオプションがあります。

  • addParametersConverter() を呼び出して追加のパラメーターを追加する

  • setParametersConverter() を呼び出してパラメーターを上書きする

  • setParametersCustomizer() を呼び出してパラメーターを完全にカスタマイズする

setParametersConverter() を使用すると、ユーザーがすべてのデフォルトパラメーターを自分で指定する必要があるため、パラメーターを完全にカスタマイズすることはできません。デフォルトパラメーターは常に提供されますが、setParametersCustomizer() を呼び出すことで完全にカスタマイズしたり省略したりできます。

addParametersConverter() を使用すると、すべてのリクエストに追加されるデフォルトのパラメーターに影響を与えずに、追加のパラメーターを含めることができます。次の例では、registrationId が keycloak の場合に、リクエストに audience パラメーターを追加します。

追加のリクエストパラメーターを含める
  • Java

  • Kotlin

RestClientJwtBearerTokenResponseClient accessTokenResponseClient =
	new RestClientJwtBearerTokenResponseClient();
accessTokenResponseClient.addParametersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
	if (clientRegistration.getRegistrationId().equals("keycloak")) {
		parameters.set(OAuth2ParameterNames.AUDIENCE, "my-audience");
	}
	return parameters;
});
val accessTokenResponseClient = RestClientJwtBearerTokenResponseClient()
accessTokenResponseClient.addParametersConverter { grantRequest ->
	val clientRegistration = grantRequest.getClientRegistration()
	val parameters = LinkedMultiValueMap<String, String>()
	if (clientRegistration.getRegistrationId() == "keycloak") {
        parameters[OAuth2ParameterNames.AUDIENCE] = "my-audience"
	}
	parameters
}

setParametersConverter() を使用してデフォルトのパラメーターを上書きできます。次の例では、registrationId が okta の場合に client_id パラメーターを上書きします。

リクエストパラメーターの上書き
  • Java

  • Kotlin

RestClientJwtBearerTokenResponseClient accessTokenResponseClient =
	new RestClientJwtBearerTokenResponseClient();
accessTokenResponseClient.setParametersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	LinkedMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
	if (clientRegistration.getRegistrationId().equals("okta")) {
		parameters.set(OAuth2ParameterNames.CLIENT_ID, "my-client");
	}
	return parameters;
});
val parametersConverter = DefaultOAuth2TokenRequestParametersConverter<JwtBearerGrantRequest>()
parametersConverter.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

val accessTokenResponseClient = RestClientJwtBearerTokenResponseClient()
accessTokenResponseClient.setParametersConverter { grantRequest ->
    val clientRegistration = grantRequest.getClientRegistration()
	val parameters = LinkedMultiValueMap<String, String>()
	if (clientRegistration.getRegistrationId() == "okta") {
        parameters[OAuth2ParameterNames.CLIENT_ID] = "my-client"
	}
	parameters
}

setParametersCustomizer() を使用すると、パラメーターを完全にカスタマイズできます (デフォルトパラメーターの省略を含む)。次の例では、リクエストに client_assertion パラメーターが存在する場合に client_id パラメーターを省略します。

リクエストパラメーターを省略する
  • Java

  • Kotlin

RestClientJwtBearerTokenResponseClient accessTokenResponseClient =
	new RestClientJwtBearerTokenResponseClient();
accessTokenResponseClient.setParametersCustomizer(parameters -> {
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID);
	}
});
val accessTokenResponseClient = RestClientJwtBearerTokenResponseClient()
accessTokenResponseClient.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

アクセストークンレスポンスのカスタマイズ

RestClientJwtBearerTokenResponseClient は、OAuth 2.0 アクセストークンレスポンスのレスポンスパラメーターとエラー処理をカスタマイズするためのフックを提供します。

RestClient のカスタマイズ

事前設定された RestClient を setRestClient() に提供することで、トークンレスポンスをカスタマイズできます。デフォルトの RestClient は次のように設定されています。

デフォルトの RestClient 構成
  • Java

  • Kotlin

RestClient restClient = RestClient.builder()
	.messageConverters(messageConverters -> {
		messageConverters.clear();
		messageConverters.add(new FormHttpMessageConverter());
		messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());
	})
	.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())
	.build();

RestClientJwtBearerTokenResponseClient accessTokenResponseClient =
	new RestClientJwtBearerTokenResponseClient();
accessTokenResponseClient.setRestClient(restClient);
val restClient = RestClient.builder()
	.messageConverters { messageConverters ->
		messageConverters.clear()
		messageConverters.add(FormHttpMessageConverter())
		messageConverters.add(OAuth2AccessTokenResponseHttpMessageConverter())
	}
	.defaultStatusHandler(OAuth2ErrorResponseErrorHandler())
	.build()

val accessTokenResponseClient = RestClientJwtBearerTokenResponseClient()
accessTokenResponseClient.setRestClient(restClient)

OAuth2AccessTokenResponseHttpMessageConverter は、OAuth 2.0 アクセストークンレスポンスの HttpMessageConverter です。setAccessTokenResponseConverter() を呼び出すことで、トークンレスポンスパラメーターの OAuth2AccessTokenResponse への変換をカスタマイズできます。デフォルトの実装は DefaultMapOAuth2AccessTokenResponseConverter です。

OAuth2ErrorResponseErrorHandler は、400 Bad Request などの OAuth 2.0 エラーを処理できる ResponseErrorHandler です。OAuth2ErrorHttpMessageConverter を使用して、OAuth 2.0 エラーパラメーターを OAuth2Error に変換します。setErrorConverter() を呼び出すことで、トークンレスポンスパラメーターの OAuth2Error への変換をカスタマイズできます。

Spring MVC FormHttpMessageConverter は、OAuth 2.0 アクセストークンリクエストを送信するときに使用されるため必須です。

レスポンスパラメーターのカスタマイズ

次の例は、トークンレスポンスパラメーターを OAuth2AccessTokenResponse に変換することをカスタマイズするための出発点を示しています。

アクセストークンレスポンスコンバーターをカスタマイズする
  • Java

  • Kotlin

OAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseMessageConverter =
	new OAuth2AccessTokenResponseHttpMessageConverter();
accessTokenResponseMessageConverter.setAccessTokenResponseConverter(parameters -> {
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build();
});
val accessTokenResponseMessageConverter = OAuth2AccessTokenResponseHttpMessageConverter()
accessTokenResponseMessageConverter.setAccessTokenResponseConverter { parameters ->
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build()
}

エラー処理のカスタマイズ

次の例は、Error パラメーターから OAuth2Error への変換をカスタマイズするための出発点を示しています。

アクセストークンエラーハンドラーをカスタマイズする
  • Java

  • Kotlin

OAuth2ErrorHttpMessageConverter errorConverter =
	new OAuth2ErrorHttpMessageConverter();
errorConverter.setErrorConverter(parameters -> {
	// ...
	return new OAuth2Error("custom-error", "custom description", "custom-uri");
});

OAuth2ErrorResponseErrorHandler errorHandler =
	new OAuth2ErrorResponseErrorHandler();
errorHandler.setErrorConverter(errorConverter);
val errorConverter = OAuth2ErrorHttpMessageConverter()
errorConverter.setErrorConverter { parameters ->
	// ...
	return OAuth2Error("custom-error", "custom description", "custom-uri")
}

val errorHandler = OAuth2ErrorResponseErrorHandler()
errorHandler.setErrorConverter(errorConverter)

ビルダーを使用してカスタマイズする

RestClientJwtBearerTokenResponseClient をカスタマイズするか、独自の OAuth2AccessTokenResponseClient 実装を提供するかにかかわらず、次のように OAuth2AuthorizedClientProviderBuilder を使用して構成できます ( Bean を公開する代わりに)。

ビルダーによるアクセストークンレスポンスの構成
  • Java

  • Kotlin

// Customize
OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient = ...

JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider();
jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient);

OAuth2AuthorizedClientProvider authorizedClientProvider =
		OAuth2AuthorizedClientProviderBuilder.builder()
				.provider(jwtBearerAuthorizedClientProvider)
				.build();

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val jwtBearerTokenResponseClient: OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> = ...

val jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider()
jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient)

val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
        .provider(jwtBearerAuthorizedClientProvider)
        .build()

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

アクセストークンの使用

OAuth 2.0 クライアント登録用の次の Spring Boot プロパティがあるとします。

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: urn:ietf:params:oauth:grant-type:jwt-bearer
            scope: read
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

…そして OAuth2AuthorizedClientManager @Bean:

  • Java

  • Kotlin

@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
		ClientRegistrationRepository clientRegistrationRepository,
		OAuth2AuthorizedClientRepository authorizedClientRepository) {

	JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =
			new JwtBearerOAuth2AuthorizedClientProvider();

	OAuth2AuthorizedClientProvider authorizedClientProvider =
			OAuth2AuthorizedClientProviderBuilder.builder()
					.provider(jwtBearerAuthorizedClientProvider)
					.build();

	DefaultOAuth2AuthorizedClientManager authorizedClientManager =
			new DefaultOAuth2AuthorizedClientManager(
					clientRegistrationRepository, authorizedClientRepository);
	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

	return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ClientRegistrationRepository,
        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
    val jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider()
    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .provider(jwtBearerAuthorizedClientProvider)
            .build()
    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
    return authorizedClientManager
}

OAuth2AccessToken は次のようにして入手できます。

  • Java

  • Kotlin

@RestController
public class OAuth2ResourceServerController {

	@Autowired
	private OAuth2AuthorizedClientManager authorizedClientManager;

	@GetMapping("/resource")
	public String resource(JwtAuthenticationToken jwtAuthentication) {
		OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
				.principal(jwtAuthentication)
				.build();
		OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);
		OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

		// ...

	}
}
class OAuth2ResourceServerController {

    @Autowired
    private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager

    @GetMapping("/resource")
    fun resource(jwtAuthentication: JwtAuthenticationToken?): String {
        val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(jwtAuthentication)
                .build()
        val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
        val accessToken: OAuth2AccessToken = authorizedClient.accessToken

        // ...

    }
}

JwtBearerOAuth2AuthorizedClientProvider は、デフォルトで OAuth2AuthorizationContext.getPrincipal().getPrincipal() を介して Jwt アサーションを解決するため、前の例では JwtAuthenticationToken を使用します。

別のソースから Jwt アサーションを解決する必要がある場合は、JwtBearerOAuth2AuthorizedClientProvider.setJwtAssertionResolver() にカスタム Function<OAuth2AuthorizationContext, Jwt> を提供できます。

トークン取引所

トークン交換 [IETF] (英語) 付与の詳細については、「OAuth 2.0 トークン交換」を参照してください。

アクセストークンのリクエスト

トークン交換の付与については、トークン交換のリクエストとレスポンス [IETF] (英語) プロトコルフローを参照してください。

トークン交換認可の OAuth2AccessTokenResponseClient のデフォルト実装は RestClientTokenExchangeTokenResponseClient です。これは、RestClient インスタンスを使用して、認可サーバーのトークンエンドポイントでアクセストークンを取得します。

RestClientTokenExchangeTokenResponseClient は非常に柔軟性が高く、トークン交換許可に対する OAuth 2.0 アクセストークンのリクエストとレスポンスをカスタマイズするためのオプションをいくつか提供します。詳細については、次のユースケースから選択してください。

アクセストークンリクエストのカスタマイズ

RestClientTokenExchangeTokenResponseClient は、OAuth 2.0 アクセストークンリクエストの HTTP ヘッダーとリクエストパラメーターをカスタマイズするためのフックを提供します。

リクエストヘッダーのカスタマイズ

HTTP ヘッダーをカスタマイズするには、次の 2 つのオプションがあります。

  • addHeadersConverter() を呼び出して追加のヘッダーを追加する

  • setHeadersConverter() を呼び出してヘッダーを完全にカスタマイズする

addHeadersConverter() を使用すると、すべてのリクエストに追加されるデフォルトのヘッダーに影響を与えずに、追加のヘッダーを含めることができます。次の例では、registrationId が spring の場合にリクエストに User-Agent ヘッダーを追加します。

追加の HTTP ヘッダーを含める
  • Java

  • Kotlin

RestClientTokenExchangeTokenResponseClient accessTokenResponseClient =
	new RestClientTokenExchangeTokenResponseClient();
accessTokenResponseClient.addHeadersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	HttpHeaders headers = new HttpHeaders();
	if (clientRegistration.getRegistrationId().equals("spring")) {
		headers.set(HttpHeaders.USER_AGENT, "my-user-agent");
	}
	return headers;
});
val accessTokenResponseClient = RestClientTokenExchangeTokenResponseClient()
accessTokenResponseClient.addHeadersConverter { grantRequest ->
	val clientRegistration = grantRequest.getClientRegistration()
	val headers = HttpHeaders()
	if (clientRegistration.getRegistrationId() == "spring") {
        headers[HttpHeaders.USER_AGENT] = "my-user-agent"
	}
	headers
}

DefaultOAuth2TokenRequestHeadersConverter を再利用するか、setHeadersConverter() を使用してカスタム実装を提供することで、ヘッダーを完全にカスタマイズできます。次の例では、DefaultOAuth2TokenRequestHeadersConverter を再利用し、encodeClientCredentials を無効にして、HTTP 基本認証情報が application/x-www-form-urlencoded でエンコードされないようにしています。

HTTP ヘッダーをカスタマイズする
  • Java

  • Kotlin

DefaultOAuth2TokenRequestHeadersConverter headersConverter =
	new DefaultOAuth2TokenRequestHeadersConverter();
headersConverter.setEncodeClientCredentials(false);

RestClientTokenExchangeTokenResponseClient accessTokenResponseClient =
	new RestClientTokenExchangeTokenResponseClient();
accessTokenResponseClient.setHeadersConverter(headersConverter);
val headersConverter = DefaultOAuth2TokenRequestHeadersConverter()
headersConverter.setEncodeClientCredentials(false)

val accessTokenResponseClient = RestClientTokenExchangeTokenResponseClient()
accessTokenResponseClient.setHeadersConverter(headersConverter)

リクエストパラメーターのカスタマイズ

リクエストパラメーターをカスタマイズするには、次の 3 つのオプションがあります。

  • addParametersConverter() を呼び出して追加のパラメーターを追加する

  • setParametersConverter() を呼び出してパラメーターを上書きする

  • setParametersCustomizer() を呼び出してパラメーターを完全にカスタマイズする

setParametersConverter() を使用すると、ユーザーがすべてのデフォルトパラメーターを自分で指定する必要があるため、パラメーターを完全にカスタマイズすることはできません。デフォルトパラメーターは常に提供されますが、setParametersCustomizer() を呼び出すことで完全にカスタマイズしたり省略したりできます。

addParametersConverter() を使用すると、すべてのリクエストに追加されるデフォルトのパラメーターに影響を与えずに、追加のパラメーターを含めることができます。次の例では、registrationId が keycloak の場合に、リクエストに audience パラメーターを追加します。

追加のリクエストパラメーターを含める
  • Java

  • Kotlin

RestClientTokenExchangeTokenResponseClient accessTokenResponseClient =
	new RestClientTokenExchangeTokenResponseClient();
accessTokenResponseClient.addParametersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
	if (clientRegistration.getRegistrationId().equals("keycloak")) {
		parameters.set(OAuth2ParameterNames.AUDIENCE, "my-audience");
	}
	return parameters;
});
val accessTokenResponseClient = RestClientTokenExchangeTokenResponseClient()
accessTokenResponseClient.addParametersConverter { grantRequest ->
	val clientRegistration = grantRequest.getClientRegistration()
	val parameters = LinkedMultiValueMap<String, String>()
	if (clientRegistration.getRegistrationId() == "keycloak") {
        parameters[OAuth2ParameterNames.AUDIENCE] = "my-audience"
	}
	parameters
}

setParametersConverter() を使用してデフォルトのパラメーターを上書きできます。次の例では、registrationId が okta の場合に client_id パラメーターを上書きします。

リクエストパラメーターの上書き
  • Java

  • Kotlin

RestClientTokenExchangeTokenResponseClient accessTokenResponseClient =
	new RestClientTokenExchangeTokenResponseClient();
accessTokenResponseClient.setParametersConverter(grantRequest -> {
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
	LinkedMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
	if (clientRegistration.getRegistrationId().equals("okta")) {
		parameters.set(OAuth2ParameterNames.CLIENT_ID, "my-client");
	}
	return parameters;
});
val parametersConverter = DefaultOAuth2TokenRequestParametersConverter<TokenExchangeGrantRequest>()
parametersConverter.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

val accessTokenResponseClient = RestClientTokenExchangeTokenResponseClient()
accessTokenResponseClient.setParametersConverter { grantRequest ->
    val clientRegistration = grantRequest.getClientRegistration()
	val parameters = LinkedMultiValueMap<String, String>()
	if (clientRegistration.getRegistrationId() == "okta") {
        parameters[OAuth2ParameterNames.CLIENT_ID] = "my-client"
	}
	parameters
}

setParametersCustomizer() を使用すると、パラメーターを完全にカスタマイズできます (デフォルトパラメーターの省略を含む)。次の例では、リクエストに client_assertion パラメーターが存在する場合に client_id パラメーターを省略します。

リクエストパラメーターを省略する
  • Java

  • Kotlin

RestClientTokenExchangeTokenResponseClient accessTokenResponseClient =
	new RestClientTokenExchangeTokenResponseClient();
accessTokenResponseClient.setParametersCustomizer(parameters -> {
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID);
	}
});
val accessTokenResponseClient = RestClientTokenExchangeTokenResponseClient()
accessTokenResponseClient.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

アクセストークンレスポンスのカスタマイズ

RestClientTokenExchangeTokenResponseClient は、OAuth 2.0 アクセストークンレスポンスのレスポンスパラメーターとエラー処理をカスタマイズするためのフックを提供します。

RestClient のカスタマイズ

事前設定された RestClient を setRestClient() に提供することで、トークンレスポンスをカスタマイズできます。デフォルトの RestClient は次のように設定されています。

デフォルトの RestClient 構成
  • Java

  • Kotlin

RestClient restClient = RestClient.builder()
	.messageConverters(messageConverters -> {
		messageConverters.clear();
		messageConverters.add(new FormHttpMessageConverter());
		messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());
	})
	.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())
	.build();

RestClientTokenExchangeTokenResponseClient accessTokenResponseClient =
	new RestClientTokenExchangeTokenResponseClient();
accessTokenResponseClient.setRestClient(restClient);
val restClient = RestClient.builder()
	.messageConverters { messageConverters ->
		messageConverters.clear()
		messageConverters.add(FormHttpMessageConverter())
		messageConverters.add(OAuth2AccessTokenResponseHttpMessageConverter())
	}
	.defaultStatusHandler(OAuth2ErrorResponseErrorHandler())
	.build()

val accessTokenResponseClient = RestClientTokenExchangeTokenResponseClient()
accessTokenResponseClient.setRestClient(restClient)

OAuth2AccessTokenResponseHttpMessageConverter は、OAuth 2.0 アクセストークンレスポンスの HttpMessageConverter です。setAccessTokenResponseConverter() を呼び出すことで、トークンレスポンスパラメーターの OAuth2AccessTokenResponse への変換をカスタマイズできます。デフォルトの実装は DefaultMapOAuth2AccessTokenResponseConverter です。

OAuth2ErrorResponseErrorHandler は、400 Bad Request などの OAuth 2.0 エラーを処理できる ResponseErrorHandler です。OAuth2ErrorHttpMessageConverter を使用して、OAuth 2.0 エラーパラメーターを OAuth2Error に変換します。setErrorConverter() を呼び出すことで、トークンレスポンスパラメーターの OAuth2Error への変換をカスタマイズできます。

Spring MVC FormHttpMessageConverter は、OAuth 2.0 アクセストークンリクエストを送信するときに使用されるため必須です。

レスポンスパラメーターのカスタマイズ

次の例は、トークンレスポンスパラメーターを OAuth2AccessTokenResponse に変換することをカスタマイズするための出発点を示しています。

アクセストークンレスポンスコンバーターをカスタマイズする
  • Java

  • Kotlin

OAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseMessageConverter =
	new OAuth2AccessTokenResponseHttpMessageConverter();
accessTokenResponseMessageConverter.setAccessTokenResponseConverter(parameters -> {
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build();
});
val accessTokenResponseMessageConverter = OAuth2AccessTokenResponseHttpMessageConverter()
accessTokenResponseMessageConverter.setAccessTokenResponseConverter { parameters ->
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build()
}

エラー処理のカスタマイズ

次の例は、Error パラメーターから OAuth2Error への変換をカスタマイズするための出発点を示しています。

アクセストークンエラーハンドラーをカスタマイズする
  • Java

  • Kotlin

OAuth2ErrorHttpMessageConverter errorConverter =
	new OAuth2ErrorHttpMessageConverter();
errorConverter.setErrorConverter(parameters -> {
	// ...
	return new OAuth2Error("custom-error", "custom description", "custom-uri");
});

OAuth2ErrorResponseErrorHandler errorHandler =
	new OAuth2ErrorResponseErrorHandler();
errorHandler.setErrorConverter(errorConverter);
val errorConverter = OAuth2ErrorHttpMessageConverter()
errorConverter.setErrorConverter { parameters ->
	// ...
	return OAuth2Error("custom-error", "custom description", "custom-uri")
}

val errorHandler = OAuth2ErrorResponseErrorHandler()
errorHandler.setErrorConverter(errorConverter)

ビルダーを使用してカスタマイズする

RestClientTokenExchangeTokenResponseClient をカスタマイズするか、独自の OAuth2AccessTokenResponseClient 実装を提供するかにかかわらず、次のように OAuth2AuthorizedClientProviderBuilder を使用して構成できます ( Bean を公開する代わりに)。

ビルダーによるアクセストークンレスポンスの構成
  • Java

  • Kotlin

// Customize
OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeTokenResponseClient = ...

TokenExchangeOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider = new TokenExchangeOAuth2AuthorizedClientProvider();
tokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeTokenResponseClient);

OAuth2AuthorizedClientProvider authorizedClientProvider =
		OAuth2AuthorizedClientProviderBuilder.builder()
				.provider(tokenExchangeAuthorizedClientProvider)
				.build();

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val tokenExchangeTokenResponseClient: OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> = ...

val tokenExchangeAuthorizedClientProvider = TokenExchangeOAuth2AuthorizedClientProvider()
tokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeTokenResponseClient)

val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
        .provider(tokenExchangeAuthorizedClientProvider)
        .build()

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

アクセストークンの使用

OAuth 2.0 クライアント登録用の次の Spring Boot プロパティがあるとします。

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: urn:ietf:params:oauth:grant-type:token-exchange
            scope: read
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

…そして OAuth2AuthorizedClientManager @Bean:

  • Java

  • Kotlin

@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
		ClientRegistrationRepository clientRegistrationRepository,
		OAuth2AuthorizedClientRepository authorizedClientRepository) {

	TokenExchangeOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider =
			new TokenExchangeOAuth2AuthorizedClientProvider();

	OAuth2AuthorizedClientProvider authorizedClientProvider =
			OAuth2AuthorizedClientProviderBuilder.builder()
					.provider(tokenExchangeAuthorizedClientProvider)
					.build();

	DefaultOAuth2AuthorizedClientManager authorizedClientManager =
			new DefaultOAuth2AuthorizedClientManager(
					clientRegistrationRepository, authorizedClientRepository);
	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

	return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ClientRegistrationRepository,
        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
    val tokenExchangeAuthorizedClientProvider = TokenExchangeOAuth2AuthorizedClientProvider()
    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .provider(tokenExchangeAuthorizedClientProvider)
            .build()
    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
    return authorizedClientManager
}

OAuth2AccessToken は次のようにして入手できます。

  • Java

  • Kotlin

@RestController
public class OAuth2ResourceServerController {

	@Autowired
	private OAuth2AuthorizedClientManager authorizedClientManager;

	@GetMapping("/resource")
	public String resource(JwtAuthenticationToken jwtAuthentication) {
		OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
				.principal(jwtAuthentication)
				.build();
		OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);
		OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

		// ...

	}
}
class OAuth2ResourceServerController {

    @Autowired
    private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager

    @GetMapping("/resource")
    fun resource(jwtAuthentication: JwtAuthenticationToken?): String {
        val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(jwtAuthentication)
                .build()
        val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
        val accessToken: OAuth2AccessToken = authorizedClient.accessToken

        // ...

    }
}

TokenExchangeOAuth2AuthorizedClientProvider は、デフォルトで OAuth2AuthorizationContext.getPrincipal().getPrincipal() 経由でサブジェクトトークンを ( OAuth2Token として) 解決するため、前の例では JwtAuthenticationToken が使用されます。アクタートークンはデフォルトでは解決されません。

別のソースからのサブジェクトトークンを解決する必要がある場合は、TokenExchangeOAuth2AuthorizedClientProvider.setSubjectTokenResolver() にカスタム Function<OAuth2AuthorizationContext, OAuth2Token> を提供できます。

アクタートークンを解決する必要がある場合は、TokenExchangeOAuth2AuthorizedClientProvider.setActorTokenResolver() にカスタム Function<OAuth2AuthorizationContext, OAuth2Token> を提供できます。