Skip to content

Commit 77ded9e

Browse files
committed
Added the ability to pass in a parameter when using JwtIssuerAuthenticationManagerResolver
1 parent 006f638 commit 77ded9e

File tree

2 files changed

+97
-3
lines changed

2 files changed

+97
-3
lines changed

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolver.java

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@
3030
import org.springframework.core.convert.converter.Converter;
3131
import org.springframework.core.log.LogMessage;
3232
import org.springframework.lang.NonNull;
33+
import org.springframework.security.authentication.AbstractAuthenticationToken;
3334
import org.springframework.security.authentication.AuthenticationManager;
3435
import org.springframework.security.authentication.AuthenticationManagerResolver;
3536
import org.springframework.security.core.Authentication;
3637
import org.springframework.security.core.AuthenticationException;
3738
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
39+
import org.springframework.security.oauth2.jwt.Jwt;
3840
import org.springframework.security.oauth2.jwt.JwtDecoder;
3941
import org.springframework.security.oauth2.jwt.JwtDecoders;
4042
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
@@ -93,7 +95,40 @@ public static JwtIssuerAuthenticationManagerResolver fromTrustedIssuers(Collecti
9395
public static JwtIssuerAuthenticationManagerResolver fromTrustedIssuers(Predicate<String> trustedIssuers) {
9496
Assert.notNull(trustedIssuers, "trustedIssuers cannot be null");
9597
return new JwtIssuerAuthenticationManagerResolver(
96-
new TrustedIssuerJwtAuthenticationManagerResolver(trustedIssuers));
98+
new TrustedIssuerJwtAuthenticationManagerResolver(null, trustedIssuers));
99+
}
100+
101+
/**
102+
* Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided
103+
* parameters
104+
* @param trustedIssuers an array of trusted issuers
105+
* @since 6.2
106+
*/
107+
public static JwtIssuerAuthenticationManagerResolver fromTrustedIssuers(Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter, String... trustedIssuers) {
108+
return fromTrustedIssuers(jwtAuthenticationConverter, Set.of(trustedIssuers));
109+
}
110+
111+
/**
112+
* Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided
113+
* parameters
114+
* @param trustedIssuers a collection of trusted issuers
115+
* @since 6.2
116+
*/
117+
public static JwtIssuerAuthenticationManagerResolver fromTrustedIssuers(Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter, Collection<String> trustedIssuers) {
118+
Assert.notEmpty(trustedIssuers, "trustedIssuers cannot be empty");
119+
return fromTrustedIssuers(jwtAuthenticationConverter, Set.copyOf(trustedIssuers)::contains);
120+
}
121+
122+
/**
123+
* Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided
124+
* parameters
125+
* @param trustedIssuers a predicate to validate issuers
126+
* @since 6.2
127+
*/
128+
public static JwtIssuerAuthenticationManagerResolver fromTrustedIssuers(Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter, Predicate<String> trustedIssuers) {
129+
Assert.notNull(trustedIssuers, "trustedIssuers cannot be null");
130+
return new JwtIssuerAuthenticationManagerResolver(
131+
new TrustedIssuerJwtAuthenticationManagerResolver(jwtAuthenticationConverter, trustedIssuers));
97132
}
98133

99134
/**
@@ -117,6 +152,7 @@ public static JwtIssuerAuthenticationManagerResolver fromTrustedIssuers(Predicat
117152
* {@link AuthenticationManager} by the issuer
118153
*/
119154
public JwtIssuerAuthenticationManagerResolver(
155+
120156
AuthenticationManagerResolver<String> issuerAuthenticationManagerResolver) {
121157
Assert.notNull(issuerAuthenticationManagerResolver, "issuerAuthenticationManagerResolver cannot be null");
122158
this.authenticationManager = new ResolvingAuthenticationManager(issuerAuthenticationManagerResolver);
@@ -197,7 +233,14 @@ static class TrustedIssuerJwtAuthenticationManagerResolver implements Authentica
197233

198234
private final Predicate<String> trustedIssuer;
199235

236+
private final Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter;
237+
200238
TrustedIssuerJwtAuthenticationManagerResolver(Predicate<String> trustedIssuer) {
239+
this(null, trustedIssuer);
240+
}
241+
242+
TrustedIssuerJwtAuthenticationManagerResolver(Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter, Predicate<String> trustedIssuer) {
243+
this.jwtAuthenticationConverter = jwtAuthenticationConverter;
201244
this.trustedIssuer = trustedIssuer;
202245
}
203246

@@ -208,7 +251,11 @@ public AuthenticationManager resolve(String issuer) {
208251
(k) -> {
209252
this.logger.debug("Constructing AuthenticationManager");
210253
JwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(issuer);
211-
return new JwtAuthenticationProvider(jwtDecoder)::authenticate;
254+
JwtAuthenticationProvider provider = new JwtAuthenticationProvider(jwtDecoder);
255+
if (jwtAuthenticationConverter != null) {
256+
provider.setJwtAuthenticationConverter(jwtAuthenticationConverter);
257+
}
258+
return provider::authenticate;
212259
});
213260
this.logger.debug(LogMessage.format("Resolved AuthenticationManager for issuer '%s'", issuer));
214261
return authenticationManager;

oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolverTests.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package org.springframework.security.oauth2.server.resource.authentication;
1818

19-
import java.util.Collection;
19+
import java.util.Collection;
2020
import java.util.Collections;
2121
import java.util.HashMap;
2222
import java.util.Map;
@@ -29,17 +29,22 @@
2929
import com.nimbusds.jose.crypto.RSASSASigner;
3030
import com.nimbusds.jwt.JWTClaimsSet;
3131
import com.nimbusds.jwt.PlainJWT;
32+
import jakarta.servlet.http.HttpServletRequest;
3233
import net.minidev.json.JSONObject;
3334
import okhttp3.mockwebserver.MockResponse;
3435
import okhttp3.mockwebserver.MockWebServer;
36+
import org.jspecify.annotations.Nullable;
3537
import org.junit.jupiter.api.Test;
3638

39+
import org.springframework.core.convert.converter.Converter;
40+
import org.springframework.security.authentication.AbstractAuthenticationToken;
3741
import org.springframework.security.authentication.AuthenticationManager;
3842
import org.springframework.security.authentication.AuthenticationManagerResolver;
3943
import org.springframework.security.core.Authentication;
4044
import org.springframework.security.core.AuthenticationException;
4145
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
4246
import org.springframework.security.oauth2.jose.TestKeys;
47+
import org.springframework.security.oauth2.jwt.Jwt;
4348
import org.springframework.security.oauth2.jwt.JwtClaimNames;
4449
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
4550
import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver.TrustedIssuerJwtAuthenticationManagerResolver;
@@ -51,6 +56,7 @@
5156
import static org.mockito.BDDMockito.given;
5257
import static org.mockito.BDDMockito.mock;
5358
import static org.mockito.BDDMockito.verify;
59+
import static org.mockito.Mockito.when;
5460

5561
/**
5662
* Tests for {@link JwtIssuerAuthenticationManagerResolver}
@@ -267,6 +273,47 @@ public void resolveWhenBearerTokenEvilThenGenericException() {
267273
// @formatter:on
268274
}
269275

276+
@Test
277+
public void testFactoryMethodWithConverter() throws Exception {
278+
Converter<Jwt, ? extends AbstractAuthenticationToken> converter = new Converter<Jwt, AbstractAuthenticationToken>() {
279+
@Override
280+
public @Nullable AbstractAuthenticationToken convert(Jwt source) {
281+
JwtAuthenticationToken authenticationToken = new JwtAuthenticationToken(source, Collections.emptyList(), "test_translated_name");
282+
authenticationToken.setDetails("test_translated_details");
283+
return authenticationToken;
284+
}
285+
};
286+
try (MockWebServer server = new MockWebServer()) {
287+
server.start();
288+
String issuer = server.url("").toString();
289+
// @formatter:off
290+
server.enqueue(new MockResponse().setResponseCode(200)
291+
.setHeader("Content-Type", "application/json")
292+
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)
293+
));
294+
server.enqueue(new MockResponse().setResponseCode(200)
295+
.setHeader("Content-Type", "application/json")
296+
.setBody(JWK_SET)
297+
);
298+
server.enqueue(new MockResponse().setResponseCode(200)
299+
.setHeader("Content-Type", "application/json")
300+
.setBody(JWK_SET)
301+
);
302+
// @formatter:on
303+
JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
304+
new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
305+
jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
306+
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver
307+
.fromTrustedIssuers(converter, issuer);
308+
Authentication token = withBearerToken(jws.serialize());
309+
AuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null);
310+
assertThat(authenticationManager).isNotNull();
311+
Authentication authentication = authenticationManager.authenticate(token);
312+
assertThat(authentication.isAuthenticated()).isTrue();
313+
assertThat(authentication.getDetails()).isEqualTo("test_translated_details");
314+
}
315+
}
316+
270317
@Test
271318
public void resolveWhenAuthenticationExceptionThenAuthenticationRequestIsIncluded() {
272319
Authentication authentication = new BearerTokenAuthenticationToken(this.jwt);

0 commit comments

Comments
 (0)