Skip to content

Commit b9b0bb7

Browse files
committed
Add Mutual-TLS client certificate-bound access tokens
Issue gh-101 Closes gh-1560
1 parent 9bd0043 commit b9b0bb7

16 files changed

+224
-17
lines changed

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 the original author or authors.
2+
* Copyright 2020-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,11 +34,13 @@
3434
* The metadata endpoint returns a set of claims an Authorization Server describes about its configuration.
3535
*
3636
* @author Daniel Garnier-Moiroux
37+
* @author Joe Grandja
3738
* @see OAuth2AuthorizationServerMetadataClaimAccessor
3839
* @since 0.1.1
3940
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-3.2">3.2. Authorization Server Metadata Response</a>
4041
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse">4.2. OpenID Provider Configuration Response</a>
4142
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4. Device Authorization Grant Metadata</a>
43+
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8705#section-3.3">3.3 Mutual-TLS Client Certificate-Bound Access Tokens Metadata</a>
4244
*/
4345
public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth2AuthorizationServerMetadataClaimAccessor, Serializable {
4446
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
@@ -320,6 +322,17 @@ public B codeChallengeMethods(Consumer<List<String>> codeChallengeMethodsConsume
320322
return getThis();
321323
}
322324

325+
/**
326+
* Use this {@code tls_client_certificate_bound_access_tokens} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
327+
*
328+
* @param tlsClientCertificateBoundAccessTokens {@code true} to indicate support for mutual-TLS client certificate-bound access tokens
329+
* @return the {@link AbstractBuilder} for further configuration
330+
* @since 1.3
331+
*/
332+
public B tlsClientCertificateBoundAccessTokens(boolean tlsClientCertificateBoundAccessTokens) {
333+
return claim(OAuth2AuthorizationServerMetadataClaimNames.TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS, tlsClientCertificateBoundAccessTokens);
334+
}
335+
323336
/**
324337
* Use this claim in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}.
325338
*

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 the original author or authors.
2+
* Copyright 2020-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,12 +25,14 @@
2525
* used in OAuth 2.0 Authorization Server Metadata and OpenID Connect Discovery 1.0.
2626
*
2727
* @author Daniel Garnier-Moiroux
28+
* @author Joe Grandja
2829
* @since 0.1.1
2930
* @see ClaimAccessor
3031
* @see OAuth2AuthorizationServerMetadataClaimNames
3132
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-2">2. Authorization Server Metadata</a>
3233
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">3. OpenID Provider Metadata</a>
3334
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4. Device Authorization Grant Metadata</a>
35+
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8705#section-3.3">3.3 Mutual-TLS Client Certificate-Bound Access Tokens Metadata</a>
3436
*/
3537
public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAccessor {
3638

@@ -171,4 +173,14 @@ default List<String> getCodeChallengeMethods() {
171173
return getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED);
172174
}
173175

176+
/**
177+
* Returns {@code true} to indicate support for mutual-TLS client certificate-bound access tokens {@code (tls_client_certificate_bound_access_tokens)}.
178+
*
179+
* @return {@code true} to indicate support for mutual-TLS client certificate-bound access tokens, {@code false} otherwise
180+
* @since 1.3
181+
*/
182+
default boolean isTlsClientCertificateBoundAccessTokens() {
183+
return Boolean.TRUE.equals(getClaimAsBoolean(OAuth2AuthorizationServerMetadataClaimNames.TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS));
184+
}
185+
174186
}

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 the original author or authors.
2+
* Copyright 2020-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,10 +20,12 @@
2020
* used in OAuth 2.0 Authorization Server Metadata and OpenID Connect Discovery 1.0.
2121
*
2222
* @author Daniel Garnier-Moiroux
23+
* @author Joe Grandja
2324
* @since 0.1.1
2425
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-2">2. Authorization Server Metadata</a>
2526
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">3. OpenID Provider Metadata</a>
2627
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4. Device Authorization Grant Metadata</a>
28+
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8705#section-3.3">3.3 Mutual-TLS Client Certificate-Bound Access Tokens Metadata</a>
2729
*/
2830
public class OAuth2AuthorizationServerMetadataClaimNames {
2931

@@ -104,6 +106,12 @@ public class OAuth2AuthorizationServerMetadataClaimNames {
104106
*/
105107
public static final String CODE_CHALLENGE_METHODS_SUPPORTED = "code_challenge_methods_supported";
106108

109+
/**
110+
* {@code tls_client_certificate_bound_access_tokens} - {@code true} to indicate support for mutual-TLS client certificate-bound access tokens
111+
* @since 1.3
112+
*/
113+
public static final String TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS = "tls_client_certificate_bound_access_tokens";
114+
107115
protected OAuth2AuthorizationServerMetadataClaimNames() {
108116
}
109117

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java

+1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
111111
.tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint()))
112112
.tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods())
113113
.codeChallengeMethod("S256")
114+
.tlsClientCertificateBoundAccessTokens(true)
114115
.subjectType("public")
115116
.idTokenSigningAlgorithm(SignatureAlgorithm.RS256.getName())
116117
.scope(OidcScopes.OPENID);

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/ConfigurationSettingNames.java

+7
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,13 @@ public static final class Token {
189189
*/
190190
public static final String ID_TOKEN_SIGNATURE_ALGORITHM = TOKEN_SETTINGS_NAMESPACE.concat("id-token-signature-algorithm");
191191

192+
/**
193+
* Set to {@code true} if access tokens must be bound to the client {@code X509Certificate}
194+
* received during client authentication when using the {@code tls_client_auth} or {@code self_signed_tls_client_auth} method.
195+
* @since 1.3
196+
*/
197+
public static final String X509_CERTIFICATE_BOUND_ACCESS_TOKENS = TOKEN_SETTINGS_NAMESPACE.concat("x509-certificate-bound-access-tokens");
198+
192199
private Token() {
193200
}
194201

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettings.java

+27-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 the original author or authors.
2+
* Copyright 2020-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -103,6 +103,18 @@ public SignatureAlgorithm getIdTokenSignatureAlgorithm() {
103103
return getSetting(ConfigurationSettingNames.Token.ID_TOKEN_SIGNATURE_ALGORITHM);
104104
}
105105

106+
/**
107+
* Returns {@code true} if access tokens must be bound to the client {@code X509Certificate}
108+
* received during client authentication when using the {@code tls_client_auth} or {@code self_signed_tls_client_auth} method.
109+
* The default is {@code false}.
110+
*
111+
* @return {@code true} if access tokens must be bound to the client {@code X509Certificate}, {@code false} otherwise
112+
* @since 1.3
113+
*/
114+
public boolean isX509CertificateBoundAccessTokens() {
115+
return getSetting(ConfigurationSettingNames.Token.X509_CERTIFICATE_BOUND_ACCESS_TOKENS);
116+
}
117+
106118
/**
107119
* Constructs a new {@link Builder} with the default settings.
108120
*
@@ -116,7 +128,8 @@ public static Builder builder() {
116128
.deviceCodeTimeToLive(Duration.ofMinutes(5))
117129
.reuseRefreshTokens(true)
118130
.refreshTokenTimeToLive(Duration.ofMinutes(60))
119-
.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256);
131+
.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
132+
.x509CertificateBoundAccessTokens(false);
120133
}
121134

122135
/**
@@ -224,6 +237,18 @@ public Builder idTokenSignatureAlgorithm(SignatureAlgorithm idTokenSignatureAlgo
224237
return setting(ConfigurationSettingNames.Token.ID_TOKEN_SIGNATURE_ALGORITHM, idTokenSignatureAlgorithm);
225238
}
226239

240+
/**
241+
* Set to {@code true} if access tokens must be bound to the client {@code X509Certificate}
242+
* received during client authentication when using the {@code tls_client_auth} or {@code self_signed_tls_client_auth} method.
243+
*
244+
* @param x509CertificateBoundAccessTokens {@code true} if access tokens must be bound to the client {@code X509Certificate}, {@code false} otherwise
245+
* @return the {@link Builder} for further configuration
246+
* @since 1.3
247+
*/
248+
public Builder x509CertificateBoundAccessTokens(boolean x509CertificateBoundAccessTokens) {
249+
return setting(ConfigurationSettingNames.Token.X509_CERTIFICATE_BOUND_ACCESS_TOKENS, x509CertificateBoundAccessTokens);
250+
}
251+
227252
/**
228253
* Builds the {@link TokenSettings}.
229254
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2020-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.server.authorization.token;
17+
18+
import java.security.MessageDigest;
19+
import java.security.cert.X509Certificate;
20+
import java.util.Base64;
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
import java.util.function.Consumer;
24+
25+
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
26+
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
27+
import org.springframework.security.oauth2.core.OAuth2Error;
28+
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
29+
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
30+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
31+
32+
/**
33+
* @author Joe Grandja
34+
* @since 1.3
35+
*/
36+
final class DefaultOAuth2TokenClaimsConsumer implements Consumer<Map<String, Object>> {
37+
private static final ClientAuthenticationMethod TLS_CLIENT_AUTH_AUTHENTICATION_METHOD =
38+
new ClientAuthenticationMethod("tls_client_auth");
39+
private static final ClientAuthenticationMethod SELF_SIGNED_TLS_CLIENT_AUTH_AUTHENTICATION_METHOD =
40+
new ClientAuthenticationMethod("self_signed_tls_client_auth");
41+
private final OAuth2TokenContext context;
42+
43+
DefaultOAuth2TokenClaimsConsumer(OAuth2TokenContext context) {
44+
this.context = context;
45+
}
46+
47+
@Override
48+
public void accept(Map<String, Object> claims) {
49+
// Add 'cnf' claim for Mutual-TLS Client Certificate-Bound Access Tokens
50+
if (OAuth2TokenType.ACCESS_TOKEN.equals(this.context.getTokenType()) &&
51+
this.context.getAuthorizationGrant() != null &&
52+
this.context.getAuthorizationGrant().getPrincipal() instanceof OAuth2ClientAuthenticationToken clientAuthentication) {
53+
54+
if ((TLS_CLIENT_AUTH_AUTHENTICATION_METHOD.equals(clientAuthentication.getClientAuthenticationMethod()) ||
55+
SELF_SIGNED_TLS_CLIENT_AUTH_AUTHENTICATION_METHOD.equals(clientAuthentication.getClientAuthenticationMethod())) &&
56+
this.context.getRegisteredClient().getTokenSettings().isX509CertificateBoundAccessTokens()) {
57+
58+
X509Certificate[] clientCertificateChain = (X509Certificate[]) clientAuthentication.getCredentials();
59+
try {
60+
String sha256Thumbprint = computeSHA256Thumbprint(clientCertificateChain[0]);
61+
Map<String, Object> x5tClaim = new HashMap<>();
62+
x5tClaim.put("x5t#S256", sha256Thumbprint);
63+
claims.put("cnf", x5tClaim);
64+
} catch (Exception ex) {
65+
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
66+
"Failed to compute SHA-256 Thumbprint for client X509Certificate.", null);
67+
throw new OAuth2AuthenticationException(error, ex);
68+
}
69+
}
70+
}
71+
}
72+
73+
private static String computeSHA256Thumbprint(X509Certificate x509Certificate) throws Exception {
74+
MessageDigest md = MessageDigest.getInstance("SHA-256");
75+
byte[] digest = md.digest(x509Certificate.getEncoded());
76+
return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
77+
}
78+
79+
}

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 the original author or authors.
2+
* Copyright 2020-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -144,6 +144,7 @@ public Jwt generate(OAuth2TokenContext context) {
144144
}
145145
}
146146
}
147+
claimsBuilder.claims(new DefaultOAuth2TokenClaimsConsumer(context));
147148
// @formatter:on
148149

149150
JwsHeader.Builder jwsHeaderBuilder = JwsHeader.with(jwsAlgorithm);

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGenerator.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 the original author or authors.
2+
* Copyright 2020-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -84,6 +84,7 @@ public OAuth2AccessToken generate(OAuth2TokenContext context) {
8484
if (!CollectionUtils.isEmpty(context.getAuthorizedScopes())) {
8585
claimsBuilder.claim(OAuth2ParameterNames.SCOPE, context.getAuthorizedScopes());
8686
}
87+
claimsBuilder.claims(new DefaultOAuth2TokenClaimsConsumer(context));
8788
// @formatter:on
8889

8990
if (this.accessTokenCustomizer != null) {

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
106106
.tokenRevocationEndpointAuthenticationMethods(clientAuthenticationMethods())
107107
.tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint()))
108108
.tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods())
109-
.codeChallengeMethod("S256");
109+
.codeChallengeMethod("S256")
110+
.tlsClientCertificateBoundAccessTokens(true);
110111

111112
this.authorizationServerMetadataCustomizer.accept(authorizationServerMetadata);
112113

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataTests.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 the original author or authors.
2+
* Copyright 2020-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -61,6 +61,7 @@ public void buildWhenAllClaimsProvidedThenCreated() {
6161
.tokenIntrospectionEndpoint("https://example.com/oauth2/introspect")
6262
.tokenIntrospectionEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())
6363
.codeChallengeMethod("S256")
64+
.tlsClientCertificateBoundAccessTokens(true)
6465
.claim("a-claim", "a-value")
6566
.build();
6667

@@ -77,6 +78,7 @@ public void buildWhenAllClaimsProvidedThenCreated() {
7778
assertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint()).isEqualTo(url("https://example.com/oauth2/introspect"));
7879
assertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()).containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());
7980
assertThat(authorizationServerMetadata.getCodeChallengeMethods()).containsExactly("S256");
81+
assertThat(authorizationServerMetadata.isTlsClientCertificateBoundAccessTokens()).isTrue();
8082
assertThat(authorizationServerMetadata.getClaimAsString("a-claim")).isEqualTo("a-value");
8183
}
8284

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java

+1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ public void doFilterWhenConfigurationRequestThenConfigurationResponse() throws E
131131
assertThat(providerConfigurationResponse).contains("\"introspection_endpoint\":\"https://example.com/oauth2/v1/introspect\"");
132132
assertThat(providerConfigurationResponse).contains("\"introspection_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
133133
assertThat(providerConfigurationResponse).contains("\"code_challenge_methods_supported\":[\"S256\"]");
134+
assertThat(providerConfigurationResponse).contains("\"tls_client_certificate_bound_access_tokens\":true");
134135
assertThat(providerConfigurationResponse).contains("\"subject_types_supported\":[\"public\"]");
135136
assertThat(providerConfigurationResponse).contains("\"id_token_signing_alg_values_supported\":[\"RS256\"]");
136137
assertThat(providerConfigurationResponse).contains("\"userinfo_endpoint\":\"https://example.com/userinfo\"");

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettingsTests.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 the original author or authors.
2+
* Copyright 2020-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,14 +34,15 @@ public class TokenSettingsTests {
3434
@Test
3535
public void buildWhenDefaultThenDefaultsAreSet() {
3636
TokenSettings tokenSettings = TokenSettings.builder().build();
37-
assertThat(tokenSettings.getSettings()).hasSize(7);
37+
assertThat(tokenSettings.getSettings()).hasSize(8);
3838
assertThat(tokenSettings.getAuthorizationCodeTimeToLive()).isEqualTo(Duration.ofMinutes(5));
3939
assertThat(tokenSettings.getAccessTokenTimeToLive()).isEqualTo(Duration.ofMinutes(5));
4040
assertThat(tokenSettings.getAccessTokenFormat()).isEqualTo(OAuth2TokenFormat.SELF_CONTAINED);
4141
assertThat(tokenSettings.getDeviceCodeTimeToLive()).isEqualTo(Duration.ofMinutes(5));
4242
assertThat(tokenSettings.isReuseRefreshTokens()).isTrue();
4343
assertThat(tokenSettings.getRefreshTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60));
4444
assertThat(tokenSettings.getIdTokenSignatureAlgorithm()).isEqualTo(SignatureAlgorithm.RS256);
45+
assertThat(tokenSettings.isX509CertificateBoundAccessTokens()).isFalse();
4546
}
4647

4748
@Test
@@ -158,13 +159,21 @@ public void idTokenSignatureAlgorithmWhenProvidedThenSet() {
158159
assertThat(tokenSettings.getIdTokenSignatureAlgorithm()).isEqualTo(idTokenSignatureAlgorithm);
159160
}
160161

162+
@Test
163+
public void x509CertificateBoundAccessTokensWhenTrueThenSet() {
164+
TokenSettings tokenSettings = TokenSettings.builder()
165+
.x509CertificateBoundAccessTokens(true)
166+
.build();
167+
assertThat(tokenSettings.isX509CertificateBoundAccessTokens()).isTrue();
168+
}
169+
161170
@Test
162171
public void settingWhenCustomThenSet() {
163172
TokenSettings tokenSettings = TokenSettings.builder()
164173
.setting("name1", "value1")
165174
.settings(settings -> settings.put("name2", "value2"))
166175
.build();
167-
assertThat(tokenSettings.getSettings()).hasSize(9);
176+
assertThat(tokenSettings.getSettings()).hasSize(10);
168177
assertThat(tokenSettings.<String>getSetting("name1")).isEqualTo("value1");
169178
assertThat(tokenSettings.<String>getSetting("name2")).isEqualTo("value2");
170179
}

0 commit comments

Comments
 (0)