Client Authentication Support

JWT Bearer

请参阅 JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants 了解更多有关 JWT Bearer 客户端身份验证的详细信息。

Please refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on JWT Bearer Client Authentication.

JWT Bearer 客户端身份验证的默认实现为 NimbusJwtClientAuthenticationParametersConverter,它通过在 client_assertion 参数中添加经过签名的 JSON Web 令牌 (JWS) 来自定义令牌请求参数。

The default implementation for JWT Bearer Client Authentication is NimbusJwtClientAuthenticationParametersConverter, which is a Converter that customizes the Token Request parameters by adding a signed JSON Web Token (JWS) in the client_assertion parameter.

用于对 JWS 签名的 java.security.PrivateKeyjavax.crypto.SecretKey 由与 NimbusJwtClientAuthenticationParametersConverter 关联的 com.nimbusds.jose.jwk.JWK 解析器提供。

The java.security.PrivateKey or javax.crypto.SecretKey used for signing the JWS is supplied by the com.nimbusds.jose.jwk.JWK resolver associated with NimbusJwtClientAuthenticationParametersConverter.

Authenticate using private_key_jwt

给定 OAuth 2.0 客户端注册的以下 Spring Boot 2.x 属性:

Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-authentication-method: private_key_jwt
            authorization-grant-type: authorization_code
            ...

下面的示例展示了如何配置 WebClientReactiveAuthorizationCodeTokenResponseClient

The following example shows how to configure WebClientReactiveAuthorizationCodeTokenResponseClient:

  • Java

  • Kotlin

Function<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {
	if (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT)) {
		// Assuming RSA key type
		RSAPublicKey publicKey = ...
		RSAPrivateKey privateKey = ...
		return new RSAKey.Builder(publicKey)
				.privateKey(privateKey)
				.keyID(UUID.randomUUID().toString())
				.build();
	}
	return null;
};

WebClientReactiveAuthorizationCodeTokenResponseClient tokenResponseClient =
		new WebClientReactiveAuthorizationCodeTokenResponseClient();
tokenResponseClient.addParametersConverter(
		new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));
val jwkResolver: Function<ClientRegistration, JWK> =
    Function<ClientRegistration, JWK> { clientRegistration ->
        if (clientRegistration.clientAuthenticationMethod.equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT)) {
            // Assuming RSA key type
            var publicKey: RSAPublicKey = ...
            var privateKey: RSAPrivateKey = ...
            RSAKey.Builder(publicKey)
                    .privateKey(privateKey)
                    .keyID(UUID.randomUUID().toString())
                .build()
        }
        null
    }

val tokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()
tokenResponseClient.addParametersConverter(
    NimbusJwtClientAuthenticationParametersConverter(jwkResolver)
)

Authenticate using client_secret_jwt

给定 OAuth 2.0 客户端注册的以下 Spring Boot 2.x 属性:

Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            client-authentication-method: client_secret_jwt
            authorization-grant-type: client_credentials
            ...

下面的示例展示了如何配置 WebClientReactiveClientCredentialsTokenResponseClient

The following example shows how to configure WebClientReactiveClientCredentialsTokenResponseClient:

  • Java

  • Kotlin

Function<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {
	if (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.CLIENT_SECRET_JWT)) {
		SecretKeySpec secretKey = new SecretKeySpec(
				clientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8),
				"HmacSHA256");
		return new OctetSequenceKey.Builder(secretKey)
				.keyID(UUID.randomUUID().toString())
				.build();
	}
	return null;
};

WebClientReactiveClientCredentialsTokenResponseClient tokenResponseClient =
		new WebClientReactiveClientCredentialsTokenResponseClient();
tokenResponseClient.addParametersConverter(
		new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));
val jwkResolver = Function<ClientRegistration, JWK?> { clientRegistration: ClientRegistration ->
    if (clientRegistration.clientAuthenticationMethod == ClientAuthenticationMethod.CLIENT_SECRET_JWT) {
        val secretKey = SecretKeySpec(
            clientRegistration.clientSecret.toByteArray(StandardCharsets.UTF_8),
            "HmacSHA256"
        )
        OctetSequenceKey.Builder(secretKey)
            .keyID(UUID.randomUUID().toString())
            .build()
    }
    null
}

val tokenResponseClient = WebClientReactiveClientCredentialsTokenResponseClient()
tokenResponseClient.addParametersConverter(
    NimbusJwtClientAuthenticationParametersConverter(jwkResolver)
)

Customizing the JWT assertion

NimbusJwtClientAuthenticationParametersConverter 生成的 JWT 默认包含 isssubaudjtiiatexp 声明。您可以通过向 setJwtClientAssertionCustomizer() 提供 Consumer<NimbusJwtClientAuthenticationParametersConverter.JwtClientAuthenticationContext<T>> 来自定义标题和/或声明。以下示例演示如何自定义 JWT 的声明:

The JWT produced by NimbusJwtClientAuthenticationParametersConverter contains the iss, sub, aud, jti, iat and exp claims by default. You can customize the headers and/or claims by providing a Consumer<NimbusJwtClientAuthenticationParametersConverter.JwtClientAuthenticationContext<T>> to setJwtClientAssertionCustomizer(). The following example shows how to customize claims of the JWT:

  • Java

  • Kotlin

Function<ClientRegistration, JWK> jwkResolver = ...

NimbusJwtClientAuthenticationParametersConverter<OAuth2ClientCredentialsGrantRequest> converter =
		new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver);
converter.setJwtClientAssertionCustomizer((context) -> {
	context.getHeaders().header("custom-header", "header-value");
	context.getClaims().claim("custom-claim", "claim-value");
});
val jwkResolver = ...

val converter: NimbusJwtClientAuthenticationParametersConverter<OAuth2ClientCredentialsGrantRequest> =
    NimbusJwtClientAuthenticationParametersConverter(jwkResolver)
converter.setJwtClientAssertionCustomizer { context ->
    context.headers.header("custom-header", "header-value")
    context.claims.claim("custom-claim", "claim-value")
}