構成モデル

デフォルトの構成

OAuth2AuthorizationServerConfiguration は、OAuth2 認可サーバーの最小限のデフォルト構成を提供する @Configuration です。

OAuth2AuthorizationServerConfiguration は OAuth2AuthorizationServerConfigurer を使用してデフォルト設定を適用し、OAuth2 認証サーバーをサポートするすべてのインフラストラクチャコンポーネントで構成される SecurityFilterChain@Bean を登録します。

OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(HttpSecurity) は、デフォルトの OAuth2 セキュリティ構成を HttpSecurity に適用する便利な (static) ユーティリティメソッドです。

OAuth2 認証サーバー SecurityFilterChain@Bean は、次の既定のプロトコルエンドポイントで構成されます。

JWK Set エンドポイントは、JWKSource<SecurityContext>@Bean が登録されている場合にのみ構成されます。

次の例は、OAuth2AuthorizationServerConfiguration を使用して最小限のデフォルト設定を適用する方法を示しています。

@Configuration
@Import(OAuth2AuthorizationServerConfiguration.class)
public class AuthorizationServerConfig {

	@Bean
	public RegisteredClientRepository registeredClientRepository() {
		List<RegisteredClient> registrations = ...
		return new InMemoryRegisteredClientRepository(registrations);
	}

	@Bean
	public JWKSource<SecurityContext> jwkSource() {
		RSAKey rsaKey = ...
		JWKSet jwkSet = new JWKSet(rsaKey);
		return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
	}

}
authorization_code グラント [IETF] (英語) では、リソース所有者が認証される必要があります。デフォルトの OAuth2 セキュリティ構成に加えて、ユーザー認証メカニズムを構成する必要があります。

OpenID Connect 1.0 (英語) は、デフォルト設定では無効になっています。次の例は、OidcConfigurer を初期化して OpenID Connect 1.0 を有効にする方法を示しています。

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
	OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);

	http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
		.oidc(Customizer.withDefaults());	// Initialize `OidcConfigurer`

	return http.build();
}

デフォルトのプロトコルエンドポイントに加えて、OAuth2 認証サーバー SecurityFilterChain@Bean は、次の OpenID Connect 1.0 プロトコルエンドポイントで構成されます。

多くの デプロイ は動的クライアント登録を必要としないため、OpenID Connect 1.0 クライアント登録エンドポイントはデフォルトで無効になっています。
OAuth2AuthorizationServerConfiguration.jwtDecoder(JWKSource<SecurityContext>) は、OpenID Connect 1.0 UserInfo エンドポイントおよび OpenID Connect 1.0 クライアント登録エンドポイントREQUIRED である JwtDecoder@Bean を登録するために使用できる便利な (static) ユーティリティメソッドです。

次の例は、JwtDecoder@Bean を登録する方法を示しています。

@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
	return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}

OAuth2AuthorizationServerConfiguration の主な目的は、OAuth2 認可サーバーに最小限の既定の構成を適用するための便利なメソッドを提供することです。ただし、ほとんどの場合、構成のカスタマイズが必要になります。

構成のカスタマイズ

OAuth2AuthorizationServerConfigurer は、OAuth2 認証サーバーのセキュリティ構成を完全にカスタマイズする機能を提供します。これにより、使用するコアコンポーネント (たとえば、RegisteredClientRepositoryOAuth2AuthorizationServiceOAuth2TokenGenerator など) を指定できます。さらに、プロトコルエンドポイント ( 認可エンドポイントデバイス認可エンドポイントデバイス検証エンドポイントトークンエンドポイントトークンイントロスペクションエンドポイントなど) のリクエスト処理ロジックをカスタマイズできます。

OAuth2AuthorizationServerConfigurer は、次の構成オプションを提供します。

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
	OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
		new OAuth2AuthorizationServerConfigurer();
	http.apply(authorizationServerConfigurer);

	authorizationServerConfigurer
		.registeredClientRepository(registeredClientRepository) (1)
		.authorizationService(authorizationService) (2)
		.authorizationConsentService(authorizationConsentService)   (3)
		.authorizationServerSettings(authorizationServerSettings) (4)
		.tokenGenerator(tokenGenerator) (5)
		.clientAuthentication(clientAuthentication -> { })  (6)
		.authorizationEndpoint(authorizationEndpoint -> { })    (7)
		.deviceAuthorizationEndpoint(deviceAuthorizationEndpoint -> { })    (8)
		.deviceVerificationEndpoint(deviceVerificationEndpoint -> { })  (9)
		.tokenEndpoint(tokenEndpoint -> { })    (10)
		.tokenIntrospectionEndpoint(tokenIntrospectionEndpoint -> { })  (11)
		.tokenRevocationEndpoint(tokenRevocationEndpoint -> { })    (12)
		.authorizationServerMetadataEndpoint(authorizationServerMetadataEndpoint -> { })    (13)
		.oidc(oidc -> oidc
			.providerConfigurationEndpoint(providerConfigurationEndpoint -> { })    (14)
			.logoutEndpoint(logoutEndpoint -> { })  (15)
			.userInfoEndpoint(userInfoEndpoint -> { })  (16)
			.clientRegistrationEndpoint(clientRegistrationEndpoint -> { })  (17)
		);

	return http.build();
}
1registeredClientRepository(): 新規および既存のクライアントを管理するための RegisteredClientRepository ( REQUIRED )。
2authorizationService(): 新規および既存の認可を管理するための OAuth2AuthorizationService
3authorizationConsentService(): 新規および既存の認可同意を管理するための OAuth2AuthorizationConsentService
4authorizationServerSettings(): OAuth2 認証サーバーの構成設定をカスタマイズするための AuthorizationServerSettings ( REQUIRED )。
5tokenGenerator(): OAuth2 認証サーバーでサポートされているトークンを生成するための OAuth2TokenGenerator
6clientAuthentication(): OAuth2 クライアント認証の設定ツール。
7authorizationEndpoint(): OAuth2 認証エンドポイントの設定ツール。
8deviceAuthorizationEndpoint(): OAuth2 デバイス認証エンドポイントの設定ツール。
9deviceVerificationEndpoint(): OAuth2 デバイス検証エンドポイントの設定ツール。
10tokenEndpoint(): OAuth2 トークンエンドポイントの設定ツール。
11tokenIntrospectionEndpoint(): OAuth2 トークンイントロスペクションエンドポイントの設定ツール。
12tokenRevocationEndpoint(): OAuth2 トークン失効エンドポイントの設定ツール。
13authorizationServerMetadataEndpoint(): OAuth2 認証サーバーメタデータエンドポイントの設定ツール。
14providerConfigurationEndpoint(): OpenID Connect 1.0 プロバイダー構成エンドポイントの設定ツール。
15logoutEndpoint(): OpenID Connect 1.0 ログアウトエンドポイントの設定ツール。
16userInfoEndpoint(): OpenID Connect 1.0 UserInfo エンドポイントの設定ツール。
17clientRegistrationEndpoint(): OpenID Connect 1.0 クライアント登録エンドポイントの設定ツール。

認可サーバー設定の構成

AuthorizationServerSettings には、OAuth2 認証サーバーの構成設定が含まれています。これは、プロトコルエンドポイントの URI と発行者識別子 [IETF] (英語) を指定します。プロトコルエンドポイントのデフォルトの URI は次のとおりです。

public final class AuthorizationServerSettings extends AbstractSettings {

	...

	public static Builder builder() {
		return new Builder()
			.authorizationEndpoint("/oauth2/authorize")
			.deviceAuthorizationEndpoint("/oauth2/device_authorization")
			.deviceVerificationEndpoint("/oauth2/device_verification")
			.tokenEndpoint("/oauth2/token")
			.tokenIntrospectionEndpoint("/oauth2/introspect")
			.tokenRevocationEndpoint("/oauth2/revoke")
			.jwkSetEndpoint("/oauth2/jwks")
			.oidcLogoutEndpoint("/connect/logout")
			.oidcUserInfoEndpoint("/userinfo")
			.oidcClientRegistrationEndpoint("/connect/register");
	}

	...

}
AuthorizationServerSettings は REQUIRED コンポーネントです。
@Import(OAuth2AuthorizationServerConfiguration.class) は、まだ提供されていない場合、AuthorizationServerSettings@Bean を自動的に登録します。

次の例は、構成設定をカスタマイズして AuthorizationServerSettings@Bean を登録する方法を示しています。

@Bean
public AuthorizationServerSettings authorizationServerSettings() {
	return AuthorizationServerSettings.builder()
		.issuer("https://example.com")
		.authorizationEndpoint("/oauth2/v1/authorize")
		.deviceAuthorizationEndpoint("/oauth2/v1/device_authorization")
		.deviceVerificationEndpoint("/oauth2/v1/device_verification")
		.tokenEndpoint("/oauth2/v1/token")
		.tokenIntrospectionEndpoint("/oauth2/v1/introspect")
		.tokenRevocationEndpoint("/oauth2/v1/revoke")
		.jwkSetEndpoint("/oauth2/v1/jwks")
		.oidcLogoutEndpoint("/connect/v1/logout")
		.oidcUserInfoEndpoint("/connect/v1/userinfo")
		.oidcClientRegistrationEndpoint("/connect/v1/register")
		.build();
}

AuthorizationServerContext は、Authorization Server ランタイム環境の情報を保持するコンテキストオブジェクトです。AuthorizationServerSettings と「現在の」発行者 ID へのアクセスを提供します。

発行者識別子が AuthorizationServerSettings.builder().issuer(String) で構成されていない場合、現在のリクエストから解決されます。
AuthorizationServerContext は AuthorizationServerContextHolder を介してアクセスでき、ThreadLocal を使用して現在のリクエストスレッドに関連付けられます。

クライアント認証の構成

OAuth2ClientAuthenticationConfigurer は、OAuth2 クライアント認証 [IETF] (英語) をカスタマイズする機能を提供します。クライアント認証リクエストの前処理、メイン処理、後処理ロジックをカスタマイズできる拡張ポイントを定義します。

OAuth2ClientAuthenticationConfigurer は、次の構成オプションを提供します。

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
	OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
		new OAuth2AuthorizationServerConfigurer();
	http.apply(authorizationServerConfigurer);

	authorizationServerConfigurer
		.clientAuthentication(clientAuthentication ->
			clientAuthentication
				.authenticationConverter(authenticationConverter)   (1)
				.authenticationConverters(authenticationConvertersConsumer) (2)
				.authenticationProvider(authenticationProvider) (3)
				.authenticationProviders(authenticationProvidersConsumer)   (4)
				.authenticationSuccessHandler(authenticationSuccessHandler) (5)
				.errorResponseHandler(errorResponseHandler) (6)
		);

	return http.build();
}
1authenticationConverter(): クライアント資格証明を HttpServletRequest から OAuth2ClientAuthenticationToken のインスタンスに抽出しようとするときに使用される AuthenticationConverter ( プリプロセッサー ) を追加します。
2authenticationConverters(): デフォルトの List および (オプションで) 追加された AuthenticationConverter へのアクセスを提供する Consumer を設定して、特定の AuthenticationConverter を追加、削除、カスタマイズする機能を許可します。
3authenticationProvider()OAuth2ClientAuthenticationToken の認証に使用される AuthenticationProvider ( メインプロセッサー ) を追加します。
4authenticationProviders(): デフォルトの List および (オプションで) 追加された AuthenticationProvider へのアクセスを提供する Consumer を設定して、特定の AuthenticationProvider を追加、削除、カスタマイズする機能を許可します。
5authenticationSuccessHandler(): クライアント認証の成功を処理し、OAuth2ClientAuthenticationToken を SecurityContext に関連付けるために使用される AuthenticationSuccessHandler ( ポストプロセッサー )。
6errorResponseHandler(): 失敗したクライアント認証を処理し、OAuth2Error レスポンス [IETF] (英語) を返すために使用される AuthenticationFailureHandler ( ポストプロセッサー )。

OAuth2ClientAuthenticationConfigurer は OAuth2ClientAuthenticationFilter を構成し、それを OAuth2 認証サーバー SecurityFilterChain@Bean に登録します。OAuth2ClientAuthenticationFilter は、クライアント認証リクエストを処理する Filter です。

デフォルトでは、クライアント認証は OAuth2 トークンエンドポイントOAuth2 トークンイントロスペクションエンドポイントOAuth2 トークン失効エンドポイントに必要です。サポートされているクライアント認証方式は client_secret_basicclient_secret_postprivate_key_jwtclient_secret_jwttls_client_authself_signed_tls_client_authnone (パブリッククライアント) です。

OAuth2ClientAuthenticationFilter は、次のデフォルトで構成されています。

  • AuthenticationConverter —  JwtClientAssertionAuthenticationConverterX509ClientCertificateAuthenticationConverterClientSecretBasicAuthenticationConverterClientSecretPostAuthenticationConverterPublicClientAuthenticationConverter で構成される DelegatingAuthenticationConverter

  • AuthenticationManager —  JwtClientAssertionAuthenticationProviderX509ClientCertificateAuthenticationProviderClientSecretAuthenticationProviderPublicClientAuthenticationProvider で構成される AuthenticationManager

  • AuthenticationSuccessHandler — 「認証済み」 OAuth2ClientAuthenticationToken (現在の Authentication) を SecurityContext に関連付ける内部実装。

  • AuthenticationFailureHandler —  OAuth2AuthenticationException に関連付けられた OAuth2Error を使用して OAuth2 エラーレスポンスを返す内部実装。

Jwt クライアントアサーション検証のカスタマイズ

JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY は、指定された RegisteredClient に OAuth2TokenValidator<Jwt> を提供するデフォルトファクトリであり、Jwt クライアントアサーションの isssubaudexpnbf クレームを検証するために使用されます。

JwtClientAssertionDecoderFactory は、型 Function<RegisteredClient, OAuth2TokenValidator<Jwt>> のカスタムファクトリを setJwtValidatorFactory() に提供することにより、デフォルトの Jwt クライアントアサーション検証をオーバーライドする機能を提供します。

JwtClientAssertionDecoderFactory は、指定された RegisteredClient に JwtDecoder を提供する JwtClientAssertionAuthenticationProvider によって使用されるデフォルトの JwtDecoderFactory であり、OAuth2 クライアント認証中に Jwt ベアラートークンを認証するために使用されます。

JwtClientAssertionDecoderFactory をカスタマイズする一般的な使用例は、Jwt クライアントアサーションで追加のクレームを検証することです。

次の例は、Jwt クライアントアサーションの追加クレームを検証するカスタマイズされた JwtClientAssertionDecoderFactory を使用して JwtClientAssertionAuthenticationProvider を構成する方法を示しています。

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
	OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
		new OAuth2AuthorizationServerConfigurer();
	http.apply(authorizationServerConfigurer);

	authorizationServerConfigurer
		.clientAuthentication(clientAuthentication ->
			clientAuthentication
				.authenticationProviders(configureJwtClientAssertionValidator())
		);

	return http.build();
}

private Consumer<List<AuthenticationProvider>> configureJwtClientAssertionValidator() {
	return (authenticationProviders) ->
		authenticationProviders.forEach((authenticationProvider) -> {
			if (authenticationProvider instanceof JwtClientAssertionAuthenticationProvider) {
				// Customize JwtClientAssertionDecoderFactory
				JwtClientAssertionDecoderFactory jwtDecoderFactory = new JwtClientAssertionDecoderFactory();
				Function<RegisteredClient, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = (registeredClient) ->
					new DelegatingOAuth2TokenValidator<>(
						// Use default validators
						JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY.apply(registeredClient),
						// Add custom validator
						new JwtClaimValidator<>("claim", "value"::equals));
				jwtDecoderFactory.setJwtValidatorFactory(jwtValidatorFactory);

				((JwtClientAssertionAuthenticationProvider) authenticationProvider)
					.setJwtDecoderFactory(jwtDecoderFactory);
			}
		});
}

相互 TLS クライアント認証のカスタマイズ

X509ClientCertificateAuthenticationProvider は、OAuth2 クライアント認証中に ClientAuthenticationMethod.TLS_CLIENT_AUTH または ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH メソッドが使用されるときに受信されるクライアント X509Certificate チェーン を認証するために使用されます。また、TLS ハンドシェイクが正常に完了した後にクライアント X509Certificate の内容を検証するために使用される「証明書検証子」で構成されます。

PKI 相互 TLS 方式

PKI Mutual-TLS (ClientAuthenticationMethod.TLS_CLIENT_AUTH) 方式の場合、証明書検証のデフォルトの実装は、クライアントのサブジェクト識別名 X509Certificate を設定 RegisteredClient.getClientSettings.getX509CertificateSubjectDN() と照合して検証します。

クライアント X509Certificate の別の属性、たとえば サブジェクト 代替名 (SAN) エントリを検証する必要がある場合、次の例は、証明書検証のカスタム実装を使用して X509ClientCertificateAuthenticationProvider を構成する方法を示しています。

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
	OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
			new OAuth2AuthorizationServerConfigurer();
	http.apply(authorizationServerConfigurer);

	authorizationServerConfigurer
			.clientAuthentication(clientAuthentication ->
					clientAuthentication
							.authenticationProviders(configureX509ClientCertificateVerifier())
			);

	return http.build();
}

private Consumer<List<AuthenticationProvider>> configureX509ClientCertificateVerifier() {
	return (authenticationProviders) ->
			authenticationProviders.forEach((authenticationProvider) -> {
				if (authenticationProvider instanceof X509ClientCertificateAuthenticationProvider) {
					Consumer<OAuth2ClientAuthenticationContext> certificateVerifier = (clientAuthenticationContext) -> {
						OAuth2ClientAuthenticationToken clientAuthentication = clientAuthenticationContext.getAuthentication();
						RegisteredClient registeredClient = clientAuthenticationContext.getRegisteredClient();
						X509Certificate[] clientCertificateChain = (X509Certificate[]) clientAuthentication.getCredentials();
						X509Certificate clientCertificate = clientCertificateChain[0];

						// TODO Verify Subject Alternative Name (SAN) entry

					};

					((X509ClientCertificateAuthenticationProvider) authenticationProvider)
							.setCertificateVerifier(certificateVerifier);
				}
			});
}

自己署名証明書相互 TLS 方式

自己署名証明書相互 TLS (ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH) 方式の場合、証明書検証のデフォルトの実装は、設定 RegisteredClient.getClientSettings.getJwkSetUrl() を使用してクライアントの JSON Web キーセットを取得し、TLS ハンドシェイク中に受信したクライアント X509Certificate と一致するものを見つけようとします。

RegisteredClient.getClientSettings.getJwkSetUrl() 設定は、JSON Web キー (JWK) セットを介してクライアントの証明書を取得するために使用されます。証明書は、セット内の個々の JWK の x5c パラメーターで表されます。

クライアント証明書にバインドされたアクセストークン

トークンエンドポイントで Mutual-TLS クライアント認証を使用すると、認可サーバーは発行されたアクセストークンをクライアントの X509Certificate にバインドできます。バインドは、クライアントの X509Certificate の SHA-256 サムプリントを計算し、そのサムプリントをアクセストークンに関連付けることによって実現されます。例: JWT アクセストークンには、最上位の cnf (確認方法) クレーム内に、X509Certificate サムプリントを含む x5t#S256 クレームが含まれます。

アクセストークンをクライアントの X509Certificate にバインドすると、保護されたリソースへのアクセス中に所有証明メカニズムを実装できるようになります。例: 保護されたリソースは、相互 TLS 認証中に使用されるクライアントの X509Certificate を取得し、証明書の拇印がアクセストークンに関連付けられた x5t#S256 クレームと一致することを確認します。

次の例は、クライアントの証明書にバインドされたアクセストークンを有効にする方法を示しています。

RegisteredClient mtlsClient = RegisteredClient.withId(UUID.randomUUID().toString())
		.clientId("mtls-client")
		.clientAuthenticationMethod(ClientAuthenticationMethod.TLS_CLIENT_AUTH)
		.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
		.scope("scope-a")
		.clientSettings(
				ClientSettings.builder()
						.x509CertificateSubjectDN("CN=mtls-client,OU=Spring Samples,O=Spring,C=US")
						.build()
		)
		.tokenSettings(
				TokenSettings.builder()
						.x509CertificateBoundAccessTokens(true)
						.build()
		)
		.build();