Fix package tangle

Closes gh-1615
This commit is contained in:
Joe Grandja
2024-05-14 12:33:24 -04:00
parent af5284974a
commit 64b20ae3eb
8 changed files with 286 additions and 202 deletions

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.token;
package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers;
import java.security.MessageDigest;
import java.security.cert.X509Certificate;
@@ -22,7 +22,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
@@ -32,28 +31,36 @@ import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimNames;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimsContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
/**
* @author Joe Grandja
* @author Steve Riesenberg
* @since 1.3
*/
final class DefaultOAuth2TokenClaimsConsumer implements Consumer<Map<String, Object>> {
private final OAuth2TokenContext context;
final class DefaultOAuth2TokenCustomizers {
DefaultOAuth2TokenClaimsConsumer(OAuth2TokenContext context) {
this.context = context;
static OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
return (context) -> context.getClaims().claims((claims) -> customize(context, claims));
}
@Override
public void accept(Map<String, Object> claims) {
static OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer() {
return (context) -> context.getClaims().claims((claims) -> customize(context, claims));
}
private static void customize(OAuth2TokenContext tokenContext, Map<String, Object> claims) {
// Add 'cnf' claim for Mutual-TLS Client Certificate-Bound Access Tokens
if (OAuth2TokenType.ACCESS_TOKEN.equals(this.context.getTokenType()) &&
this.context.getAuthorizationGrant() != null &&
this.context.getAuthorizationGrant().getPrincipal() instanceof OAuth2ClientAuthenticationToken clientAuthentication) {
if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenContext.getTokenType()) &&
tokenContext.getAuthorizationGrant() != null &&
tokenContext.getAuthorizationGrant().getPrincipal() instanceof OAuth2ClientAuthenticationToken clientAuthentication) {
if ((ClientAuthenticationMethod.TLS_CLIENT_AUTH.equals(clientAuthentication.getClientAuthenticationMethod()) ||
ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH.equals(clientAuthentication.getClientAuthenticationMethod())) &&
this.context.getRegisteredClient().getTokenSettings().isX509CertificateBoundAccessTokens()) {
tokenContext.getRegisteredClient().getTokenSettings().isX509CertificateBoundAccessTokens()) {
X509Certificate[] clientCertificateChain = (X509Certificate[]) clientAuthentication.getCredentials();
try {
@@ -71,7 +78,7 @@ final class DefaultOAuth2TokenClaimsConsumer implements Consumer<Map<String, Obj
// Add 'act' claim for delegation use case of Token Exchange Grant.
// If more than one actor is present, we create a chain of delegation by nesting "act" claims.
if (this.context.getPrincipal() instanceof OAuth2TokenExchangeCompositeAuthenticationToken compositeAuthenticationToken) {
if (tokenContext.getPrincipal() instanceof OAuth2TokenExchangeCompositeAuthenticationToken compositeAuthenticationToken) {
Map<String, Object> currentClaims = claims;
for (OAuth2TokenExchangeActor actor : compositeAuthenticationToken.getActors()) {
Map<String, Object> actorClaims = actor.getClaims();

View File

@@ -105,10 +105,7 @@ final class OAuth2ConfigurerUtils {
if (tokenGenerator == null) {
JwtGenerator jwtGenerator = getJwtGenerator(httpSecurity);
OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer = getAccessTokenCustomizer(httpSecurity);
if (accessTokenCustomizer != null) {
accessTokenGenerator.setAccessTokenCustomizer(accessTokenCustomizer);
}
accessTokenGenerator.setAccessTokenCustomizer(getAccessTokenCustomizer(httpSecurity));
OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
if (jwtGenerator != null) {
tokenGenerator = new DelegatingOAuth2TokenGenerator(
@@ -129,10 +126,7 @@ final class OAuth2ConfigurerUtils {
JwtEncoder jwtEncoder = getJwtEncoder(httpSecurity);
if (jwtEncoder != null) {
jwtGenerator = new JwtGenerator(jwtEncoder);
OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer = getJwtCustomizer(httpSecurity);
if (jwtCustomizer != null) {
jwtGenerator.setJwtCustomizer(jwtCustomizer);
}
jwtGenerator.setJwtCustomizer(getJwtCustomizer(httpSecurity));
httpSecurity.setSharedObject(JwtGenerator.class, jwtGenerator);
}
}
@@ -170,13 +164,29 @@ final class OAuth2ConfigurerUtils {
}
private static OAuth2TokenCustomizer<JwtEncodingContext> getJwtCustomizer(HttpSecurity httpSecurity) {
final OAuth2TokenCustomizer<JwtEncodingContext> defaultJwtCustomizer = DefaultOAuth2TokenCustomizers.jwtCustomizer();
ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class, JwtEncodingContext.class);
return getOptionalBean(httpSecurity, type);
final OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer = getOptionalBean(httpSecurity, type);
if (jwtCustomizer == null) {
return defaultJwtCustomizer;
}
return (context) -> {
defaultJwtCustomizer.customize(context);
jwtCustomizer.customize(context);
};
}
private static OAuth2TokenCustomizer<OAuth2TokenClaimsContext> getAccessTokenCustomizer(HttpSecurity httpSecurity) {
final OAuth2TokenCustomizer<OAuth2TokenClaimsContext> defaultAccessTokenCustomizer = DefaultOAuth2TokenCustomizers.accessTokenCustomizer();
ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class, OAuth2TokenClaimsContext.class);
return getOptionalBean(httpSecurity, type);
OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer = getOptionalBean(httpSecurity, type);
if (accessTokenCustomizer == null) {
return defaultAccessTokenCustomizer;
}
return (context) -> {
defaultAccessTokenCustomizer.customize(context);
accessTokenCustomizer.customize(context);
};
}
static AuthorizationServerSettings getAuthorizationServerSettings(HttpSecurity httpSecurity) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 the original author or authors.
* Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -144,7 +144,6 @@ public final class JwtGenerator implements OAuth2TokenGenerator<Jwt> {
}
}
}
claimsBuilder.claims(new DefaultOAuth2TokenClaimsConsumer(context));
// @formatter:on
JwsHeader.Builder jwsHeaderBuilder = JwsHeader.with(jwsAlgorithm);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 the original author or authors.
* Copyright 2020-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -84,7 +84,6 @@ public final class OAuth2AccessTokenGenerator implements OAuth2TokenGenerator<OA
if (!CollectionUtils.isEmpty(context.getAuthorizedScopes())) {
claimsBuilder.claim(OAuth2ParameterNames.SCOPE, context.getAuthorizedScopes());
}
claimsBuilder.claims(new DefaultOAuth2TokenClaimsConsumer(context));
// @formatter:on
if (this.accessTokenCustomizer != null) {

View File

@@ -0,0 +1,239 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.JwsHeader;
import org.springframework.security.oauth2.jwt.JwtClaimNames;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.util.TestX509Certificates;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Tests for {@link DefaultOAuth2TokenCustomizers}.
*
* @author Steve Riesenberg
* @author Joe Grandja
*/
class DefaultOAuth2TokenCustomizersTests {
private static final String ISSUER_1 = "issuer-1";
private static final String ISSUER_2 = "issuer-2";
private JwsHeader.Builder jwsHeaderBuilder;
private JwtClaimsSet.Builder jwtClaimsBuilder;
@BeforeEach
void setUp() {
this.jwsHeaderBuilder = JwsHeader.with(SignatureAlgorithm.RS256);
this.jwtClaimsBuilder = JwtClaimsSet.builder().issuer(ISSUER_1);
}
@Test
void customizeWhenTokenTypeIsRefreshTokenThenNoClaimsAdded() {
// @formatter:off
JwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)
.tokenType(OAuth2TokenType.REFRESH_TOKEN)
.build();
// @formatter:on
DefaultOAuth2TokenCustomizers.jwtCustomizer().customize(tokenContext);
JwtClaimsSet jwtClaimsSet = this.jwtClaimsBuilder.build();
assertThat(jwtClaimsSet.getClaims()).containsOnly(entry(JwtClaimNames.ISS, ISSUER_1));
}
@Test
void customizeWhenAuthorizationGrantIsNullThenNoClaimsAdded() {
// @formatter:off
JwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)
.tokenType(OAuth2TokenType.ACCESS_TOKEN)
.build();
// @formatter:on
DefaultOAuth2TokenCustomizers.jwtCustomizer().customize(tokenContext);
JwtClaimsSet jwtClaimsSet = this.jwtClaimsBuilder.build();
assertThat(jwtClaimsSet.getClaims()).containsOnly(entry(JwtClaimNames.ISS, ISSUER_1));
}
@Test
void customizeWhenTokenExchangeGrantAndResourcesThenNoClaimsAdded() {
OAuth2TokenExchangeAuthenticationToken tokenExchangeAuthentication = mock(
OAuth2TokenExchangeAuthenticationToken.class);
when(tokenExchangeAuthentication.getResources()).thenReturn(Set.of("resource1", "resource2"));
// @formatter:off
JwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)
.tokenType(OAuth2TokenType.ACCESS_TOKEN)
.authorizationGrant(tokenExchangeAuthentication)
.build();
// @formatter:on
DefaultOAuth2TokenCustomizers.jwtCustomizer().customize(tokenContext);
JwtClaimsSet jwtClaimsSet = this.jwtClaimsBuilder.build();
// We do not populate claims (e.g. `aud`) based on the resource parameter
assertThat(jwtClaimsSet.getClaims()).containsOnly(entry(JwtClaimNames.ISS, ISSUER_1));
}
@Test
void customizeWhenTokenExchangeGrantAndAudiencesThenNoClaimsAdded() {
OAuth2TokenExchangeAuthenticationToken tokenExchangeAuthentication = mock(
OAuth2TokenExchangeAuthenticationToken.class);
when(tokenExchangeAuthentication.getAudiences()).thenReturn(Set.of("audience1", "audience2"));
// @formatter:off
JwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)
.tokenType(OAuth2TokenType.ACCESS_TOKEN)
.authorizationGrant(tokenExchangeAuthentication)
.build();
// @formatter:on
DefaultOAuth2TokenCustomizers.jwtCustomizer().customize(tokenContext);
JwtClaimsSet jwtClaimsSet = this.jwtClaimsBuilder.build();
// NOTE: We do not populate claims (e.g. `aud`) based on the audience parameter
assertThat(jwtClaimsSet.getClaims()).containsOnly(entry(JwtClaimNames.ISS, ISSUER_1));
}
@Test
void customizeWhenTokenExchangeGrantAndDelegationThenActClaimAdded() {
OAuth2TokenExchangeAuthenticationToken tokenExchangeAuthentication = mock(
OAuth2TokenExchangeAuthenticationToken.class);
when(tokenExchangeAuthentication.getAudiences()).thenReturn(Collections.emptySet());
Authentication subject = new TestingAuthenticationToken("subject", null);
OAuth2TokenExchangeActor actor1 = new OAuth2TokenExchangeActor(Map.of(JwtClaimNames.ISS, ISSUER_1,
JwtClaimNames.SUB, "actor1"));
OAuth2TokenExchangeActor actor2 = new OAuth2TokenExchangeActor(Map.of(JwtClaimNames.ISS, ISSUER_2,
JwtClaimNames.SUB, "actor2"));
OAuth2TokenExchangeCompositeAuthenticationToken principal = new OAuth2TokenExchangeCompositeAuthenticationToken(
subject, List.of(actor1, actor2));
// @formatter:off
JwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)
.tokenType(OAuth2TokenType.ACCESS_TOKEN)
.principal(principal)
.authorizationGrant(tokenExchangeAuthentication)
.build();
// @formatter:on
DefaultOAuth2TokenCustomizers.jwtCustomizer().customize(tokenContext);
JwtClaimsSet jwtClaimsSet = this.jwtClaimsBuilder.build();
assertThat(jwtClaimsSet.getClaims()).isNotEmpty();
assertThat(jwtClaimsSet.getClaims()).hasSize(2);
assertThat(jwtClaimsSet.getClaims().get("act")).isNotNull();
@SuppressWarnings("unchecked")
Map<String, Object> actClaim1 = (Map<String, Object>) jwtClaimsSet.getClaims().get("act");
assertThat(actClaim1.get(JwtClaimNames.ISS)).isEqualTo(ISSUER_1);
assertThat(actClaim1.get(JwtClaimNames.SUB)).isEqualTo("actor1");
@SuppressWarnings("unchecked")
Map<String, Object> actClaim2 = (Map<String, Object>) actClaim1.get("act");
assertThat(actClaim2.get(JwtClaimNames.ISS)).isEqualTo(ISSUER_2);
assertThat(actClaim2.get(JwtClaimNames.SUB)).isEqualTo("actor2");
}
@Test
void customizeWhenPKIX509ClientCertificateAndCertificateBoundAccessTokensThenX5tClaimAdded() {
// @formatter:off
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.clientAuthenticationMethod(ClientAuthenticationMethod.TLS_CLIENT_AUTH)
.clientSettings(
ClientSettings.builder()
.x509CertificateSubjectDN(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE[0].getSubjectX500Principal().getName())
.build()
)
.tokenSettings(
TokenSettings.builder()
.x509CertificateBoundAccessTokens(true)
.build()
)
.build();
// @formatter:on
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
registeredClient, ClientAuthenticationMethod.TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);
OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication =
new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null);
// @formatter:off
JwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)
.tokenType(OAuth2TokenType.ACCESS_TOKEN)
.registeredClient(registeredClient)
.authorizationGrant(clientCredentialsAuthentication)
.build();
// @formatter:on
DefaultOAuth2TokenCustomizers.jwtCustomizer().customize(tokenContext);
JwtClaimsSet jwtClaimsSet = this.jwtClaimsBuilder.build();
assertThat(jwtClaimsSet.getClaims()).isNotEmpty();
assertThat(jwtClaimsSet.getClaims()).hasSize(2);
Map<String, Object> cnfClaim = jwtClaimsSet.getClaim("cnf");
assertThat(cnfClaim).isNotEmpty();
assertThat(cnfClaim.get("x5t#S256")).isNotNull();
}
@Test
void customizeWhenSelfSignedX509ClientCertificateAndCertificateBoundAccessTokensThenX5tClaimAdded() {
// @formatter:off
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.clientAuthenticationMethod(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH)
.clientSettings(
ClientSettings.builder()
.jwkSetUrl("https://client.example.com/jwks")
.build()
)
.tokenSettings(
TokenSettings.builder()
.x509CertificateBoundAccessTokens(true)
.build()
)
.build();
// @formatter:on
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
registeredClient, ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE);
OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication =
new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null);
// @formatter:off
JwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)
.tokenType(OAuth2TokenType.ACCESS_TOKEN)
.registeredClient(registeredClient)
.authorizationGrant(clientCredentialsAuthentication)
.build();
// @formatter:on
DefaultOAuth2TokenCustomizers.jwtCustomizer().customize(tokenContext);
JwtClaimsSet jwtClaimsSet = this.jwtClaimsBuilder.build();
assertThat(jwtClaimsSet.getClaims()).isNotEmpty();
assertThat(jwtClaimsSet.getClaims()).hasSize(2);
Map<String, Object> cnfClaim = jwtClaimsSet.getClaim("cnf");
assertThat(cnfClaim).isNotEmpty();
assertThat(cnfClaim.get("x5t#S256")).isNotNull();
}
}

View File

@@ -1,128 +0,0 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.token;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Tests for {@link DefaultOAuth2TokenClaimsConsumer}.
*
* @author Steve Riesenberg
*/
public class DefaultOAuth2TokenClaimsConsumerTests {
private OAuth2TokenContext tokenContext;
private Consumer<Map<String, Object>> consumer;
@BeforeEach
public void setUp() {
this.tokenContext = mock(OAuth2TokenContext.class);
this.consumer = new DefaultOAuth2TokenClaimsConsumer(this.tokenContext);
}
@Test
public void acceptWhenTokenTypeIsRefreshTokenThenNoClaimsAdded() {
when(this.tokenContext.getTokenType()).thenReturn(OAuth2TokenType.REFRESH_TOKEN);
Map<String, Object> claims = new LinkedHashMap<>();
this.consumer.accept(claims);
assertThat(claims).isEmpty();
}
@Test
public void acceptWhenAuthorizationGrantIsNullThenNoClaimsAdded() {
when(this.tokenContext.getTokenType()).thenReturn(OAuth2TokenType.ACCESS_TOKEN);
when(this.tokenContext.getAuthorizationGrant()).thenReturn(null);
Map<String, Object> claims = new LinkedHashMap<>();
this.consumer.accept(claims);
assertThat(claims).isEmpty();
}
@Test
public void acceptWhenTokenExchangeGrantAndResourcesThenNoClaimsAdded() {
OAuth2TokenExchangeAuthenticationToken tokenExchangeAuthentication = mock(
OAuth2TokenExchangeAuthenticationToken.class);
when(tokenExchangeAuthentication.getResources()).thenReturn(Set.of("resource1", "resource2"));
when(this.tokenContext.getTokenType()).thenReturn(OAuth2TokenType.ACCESS_TOKEN);
when(this.tokenContext.getAuthorizationGrant()).thenReturn(tokenExchangeAuthentication);
Map<String, Object> claims = new LinkedHashMap<>();
this.consumer.accept(claims);
// We do not populate claims (e.g. `aud`) based on the resource parameter
assertThat(claims).isEmpty();
}
@Test
public void acceptWhenTokenExchangeGrantAndAudiencesThenNoClaimsAdded() {
OAuth2TokenExchangeAuthenticationToken tokenExchangeAuthentication = mock(
OAuth2TokenExchangeAuthenticationToken.class);
when(tokenExchangeAuthentication.getAudiences()).thenReturn(Set.of("audience1", "audience2"));
when(this.tokenContext.getTokenType()).thenReturn(OAuth2TokenType.ACCESS_TOKEN);
when(this.tokenContext.getAuthorizationGrant()).thenReturn(tokenExchangeAuthentication);
Map<String, Object> claims = new LinkedHashMap<>();
this.consumer.accept(claims);
// NOTE: We do not populate claims (e.g. `aud`) based on the audience parameter
assertThat(claims).isEmpty();
}
@Test
public void acceptWhenTokenExchangeGrantAndDelegationThenActClaimAdded() {
OAuth2TokenExchangeAuthenticationToken tokenExchangeAuthentication = mock(
OAuth2TokenExchangeAuthenticationToken.class);
when(tokenExchangeAuthentication.getAudiences()).thenReturn(Collections.emptySet());
when(this.tokenContext.getTokenType()).thenReturn(OAuth2TokenType.ACCESS_TOKEN);
when(this.tokenContext.getAuthorizationGrant()).thenReturn(tokenExchangeAuthentication);
Authentication subject = new TestingAuthenticationToken("subject", null);
OAuth2TokenExchangeActor actor1 = new OAuth2TokenExchangeActor(Map.of(OAuth2TokenClaimNames.ISS, "issuer1",
OAuth2TokenClaimNames.SUB, "actor1"));
OAuth2TokenExchangeActor actor2 = new OAuth2TokenExchangeActor(Map.of(OAuth2TokenClaimNames.ISS, "issuer2",
OAuth2TokenClaimNames.SUB, "actor2"));
OAuth2TokenExchangeCompositeAuthenticationToken principal = new OAuth2TokenExchangeCompositeAuthenticationToken(
subject, List.of(actor1, actor2));
when(this.tokenContext.getPrincipal()).thenReturn(principal);
Map<String, Object> claims = new LinkedHashMap<>();
this.consumer.accept(claims);
assertThat(claims).hasSize(1);
assertThat(claims.get("act")).isNotNull();
@SuppressWarnings("unchecked")
Map<String, Object> actClaim1 = (Map<String, Object>) claims.get("act");
assertThat(actClaim1.get(OAuth2TokenClaimNames.ISS)).isEqualTo("issuer1");
assertThat(actClaim1.get(OAuth2TokenClaimNames.SUB)).isEqualTo("actor1");
@SuppressWarnings("unchecked")
Map<String, Object> actClaim2 = (Map<String, Object>) actClaim1.get("act");
assertThat(actClaim2.get(OAuth2TokenClaimNames.ISS)).isEqualTo("issuer2");
assertThat(actClaim2.get(OAuth2TokenClaimNames.SUB)).isEqualTo("actor2");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 the original author or authors.
* Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,10 +53,8 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.security.oauth2.server.authorization.util.TestX509Certificates;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -130,26 +128,11 @@ public class JwtGeneratorTests {
@Test
public void generateWhenAccessTokenTypeThenReturnJwt() {
// @formatter:off
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.clientAuthenticationMethod(ClientAuthenticationMethod.TLS_CLIENT_AUTH)
.clientSettings(
ClientSettings.builder()
.x509CertificateSubjectDN(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE[0].getSubjectX500Principal().getName())
.build()
)
.tokenSettings(
TokenSettings.builder()
.x509CertificateBoundAccessTokens(true)
.build()
)
.build();
// @formatter:on
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
registeredClient, ClientAuthenticationMethod.TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);
registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(
OAuth2AuthorizationRequest.class.getName());
OAuth2AuthorizationCodeAuthenticationToken authentication =
@@ -342,17 +325,6 @@ public class JwtGeneratorTests {
Set<String> scopes = jwtClaimsSet.getClaim(OAuth2ParameterNames.SCOPE);
assertThat(scopes).isEqualTo(tokenContext.getAuthorizedScopes());
OAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) tokenContext.getAuthorizationGrant().getPrincipal();
if (ClientAuthenticationMethod.TLS_CLIENT_AUTH.equals(clientAuthentication.getClientAuthenticationMethod()) &&
tokenContext.getRegisteredClient().getTokenSettings().isX509CertificateBoundAccessTokens()) {
Map<String, Object> cnf = jwtClaimsSet.getClaim("cnf");
assertThat(cnf).isNotEmpty();
assertThat(cnf.get("x5t#S256")).isNotNull();
} else {
Map<String, Object> cnf = jwtClaimsSet.getClaim("cnf");
assertThat(cnf).isEmpty();
}
} else {
assertThat(jwtClaimsSet.<String>getClaim(IdTokenClaimNames.AZP)).isEqualTo(tokenContext.getRegisteredClient().getClientId());
if (tokenContext.getAuthorizationGrantType().equals(AuthorizationGrantType.AUTHORIZATION_CODE)) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 the original author or authors.
* Copyright 2020-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package org.springframework.security.oauth2.server.authorization.token;
import java.security.Principal;
import java.time.Instant;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
@@ -42,10 +41,8 @@ import org.springframework.security.oauth2.server.authorization.client.TestRegis
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;
import org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.security.oauth2.server.authorization.util.TestX509Certificates;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -117,16 +114,10 @@ public class OAuth2AccessTokenGeneratorTests {
@Test
public void generateWhenReferenceAccessTokenTypeThenReturnAccessToken() {
// @formatter:off
ClientSettings clientSettings = ClientSettings.builder()
.x509CertificateSubjectDN(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE[0].getSubjectX500Principal().getName())
.build();
TokenSettings tokenSettings = TokenSettings.builder()
.accessTokenFormat(OAuth2TokenFormat.REFERENCE)
.x509CertificateBoundAccessTokens(true)
.build();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.clientAuthenticationMethod(ClientAuthenticationMethod.TLS_CLIENT_AUTH)
.clientSettings(clientSettings)
.tokenSettings(tokenSettings)
.build();
// @formatter:on
@@ -134,8 +125,7 @@ public class OAuth2AccessTokenGeneratorTests {
Authentication principal = authorization.getAttribute(Principal.class.getName());
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
registeredClient, ClientAuthenticationMethod.TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);
registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(
OAuth2AuthorizationRequest.class.getName());
OAuth2AuthorizationCodeAuthenticationToken authentication =
@@ -179,10 +169,6 @@ public class OAuth2AccessTokenGeneratorTests {
Set<String> scopes = accessTokenClaims.getClaim(OAuth2ParameterNames.SCOPE);
assertThat(scopes).isEqualTo(tokenContext.getAuthorizedScopes());
Map<String, Object> cnf = accessTokenClaims.getClaim("cnf");
assertThat(cnf).isNotEmpty();
assertThat(cnf.get("x5t#S256")).isNotNull();
ArgumentCaptor<OAuth2TokenClaimsContext> tokenClaimsContextCaptor = ArgumentCaptor.forClass(OAuth2TokenClaimsContext.class);
verify(this.accessTokenCustomizer).customize(tokenClaimsContextCaptor.capture());