Skip to content

Commit 0947e17

Browse files
committed
Extracts the JwTDecoder to enable users customize its behavior
This commit ensures that the JwtDecoder is not a private field inside the Oidc authentication provider by extracting this class and giving the possibility to customize the way different providers are validated. Fixes: gh-6379
1 parent f234a5f commit 0947e17

File tree

6 files changed

+362
-73
lines changed

6 files changed

+362
-73
lines changed

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java

+2-37
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 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.
@@ -27,11 +27,9 @@
2727
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
2828
import org.springframework.security.oauth2.client.registration.ClientRegistration;
2929
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
30-
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
3130
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
3231
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
3332
import org.springframework.security.oauth2.core.OAuth2Error;
34-
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
3533
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
3634
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
3735
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
@@ -43,16 +41,10 @@
4341
import org.springframework.security.oauth2.jwt.JwtDecoder;
4442
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
4543
import org.springframework.security.oauth2.jwt.JwtException;
46-
import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
47-
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
4844
import org.springframework.util.Assert;
49-
import org.springframework.util.StringUtils;
5045

5146
import java.util.Collection;
5247
import java.util.Map;
53-
import java.util.concurrent.ConcurrentHashMap;
54-
55-
import static org.springframework.security.oauth2.jwt.JwtProcessors.withJwkSetUri;
5648

5749
/**
5850
* An implementation of an {@link AuthenticationProvider}
@@ -82,10 +74,9 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
8274
private static final String INVALID_STATE_PARAMETER_ERROR_CODE = "invalid_state_parameter";
8375
private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter";
8476
private static final String INVALID_ID_TOKEN_ERROR_CODE = "invalid_id_token";
85-
private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
8677
private final OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
8778
private final OAuth2UserService<OidcUserRequest, OidcUser> userService;
88-
private JwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new DefaultJwtDecoderFactory();
79+
private JwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new OidcIdTokenDecoderFactory();
8980
private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities);
9081

9182
/**
@@ -219,30 +210,4 @@ private OidcIdToken createOidcToken(ClientRegistration clientRegistration, OAuth
219210
OidcIdToken idToken = new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims());
220211
return idToken;
221212
}
222-
223-
private static class DefaultJwtDecoderFactory implements JwtDecoderFactory<ClientRegistration> {
224-
private final Map<String, JwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
225-
226-
@Override
227-
public JwtDecoder createDecoder(ClientRegistration clientRegistration) {
228-
return this.jwtDecoders.computeIfAbsent(clientRegistration.getRegistrationId(), key -> {
229-
if (!StringUtils.hasText(clientRegistration.getProviderDetails().getJwkSetUri())) {
230-
OAuth2Error oauth2Error = new OAuth2Error(
231-
MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
232-
"Failed to find a Signature Verifier for Client Registration: '" +
233-
clientRegistration.getRegistrationId() +
234-
"'. Check to ensure you have configured the JwkSet URI.",
235-
null
236-
);
237-
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
238-
}
239-
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
240-
NimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder(withJwkSetUri(jwkSetUri).build());
241-
OAuth2TokenValidator<Jwt> jwtValidator = new DelegatingOAuth2TokenValidator<>(
242-
new JwtTimestampValidator(), new OidcIdTokenValidator(clientRegistration));
243-
jwtDecoder.setJwtValidator(jwtValidator);
244-
return jwtDecoder;
245-
});
246-
}
247-
}
248213
}

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeReactiveAuthenticationManager.java

+2-36
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 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.
@@ -26,32 +26,25 @@
2626
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
2727
import org.springframework.security.oauth2.client.registration.ClientRegistration;
2828
import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;
29-
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
3029
import org.springframework.security.oauth2.core.OAuth2AccessToken;
3130
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
3231
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
3332
import org.springframework.security.oauth2.core.OAuth2Error;
34-
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
3533
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
3634
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
3735
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
3836
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
3937
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
4038
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
4139
import org.springframework.security.oauth2.core.user.OAuth2User;
42-
import org.springframework.security.oauth2.jwt.Jwt;
4340
import org.springframework.security.oauth2.jwt.JwtException;
44-
import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
45-
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
4641
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
4742
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;
4843
import org.springframework.util.Assert;
49-
import org.springframework.util.StringUtils;
5044
import reactor.core.publisher.Mono;
5145

5246
import java.util.Collection;
5347
import java.util.Map;
54-
import java.util.concurrent.ConcurrentHashMap;
5548

5649
/**
5750
* An implementation of an {@link org.springframework.security.authentication.AuthenticationProvider} for OAuth 2.0 Login,
@@ -83,15 +76,14 @@ public class OidcAuthorizationCodeReactiveAuthenticationManager implements
8376
private static final String INVALID_STATE_PARAMETER_ERROR_CODE = "invalid_state_parameter";
8477
private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter";
8578
private static final String INVALID_ID_TOKEN_ERROR_CODE = "invalid_id_token";
86-
private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
8779

8880
private final ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
8981

9082
private final ReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService;
9183

9284
private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities);
9385

94-
private ReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new DefaultJwtDecoderFactory();
86+
private ReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new ReactiveOidcIdTokenDecoderFactory();
9587

9688
public OidcAuthorizationCodeReactiveAuthenticationManager(
9789
ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient,
@@ -199,30 +191,4 @@ private Mono<OidcIdToken> createOidcToken(ClientRegistration clientRegistration,
199191
return jwtDecoder.decode(rawIdToken)
200192
.map(jwt -> new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims()));
201193
}
202-
203-
private static class DefaultJwtDecoderFactory implements ReactiveJwtDecoderFactory<ClientRegistration> {
204-
private final Map<String, ReactiveJwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
205-
206-
@Override
207-
public ReactiveJwtDecoder createDecoder(ClientRegistration clientRegistration) {
208-
return this.jwtDecoders.computeIfAbsent(clientRegistration.getRegistrationId(), key -> {
209-
if (!StringUtils.hasText(clientRegistration.getProviderDetails().getJwkSetUri())) {
210-
OAuth2Error oauth2Error = new OAuth2Error(
211-
MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
212-
"Failed to find a Signature Verifier for Client Registration: '" +
213-
clientRegistration.getRegistrationId() +
214-
"'. Check to ensure you have configured the JwkSet URI.",
215-
null
216-
);
217-
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
218-
}
219-
NimbusReactiveJwtDecoder jwtDecoder = new NimbusReactiveJwtDecoder(
220-
clientRegistration.getProviderDetails().getJwkSetUri());
221-
OAuth2TokenValidator<Jwt> jwtValidator = new DelegatingOAuth2TokenValidator<>(
222-
new JwtTimestampValidator(), new OidcIdTokenValidator(clientRegistration));
223-
jwtDecoder.setJwtValidator(jwtValidator);
224-
return jwtDecoder;
225-
});
226-
}
227-
}
228194
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2002-2019 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+
* http://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.client.oidc.authentication;
17+
18+
import org.springframework.security.oauth2.client.registration.ClientRegistration;
19+
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
20+
import org.springframework.security.oauth2.core.OAuth2Error;
21+
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
22+
import org.springframework.security.oauth2.jwt.Jwt;
23+
import org.springframework.security.oauth2.jwt.JwtDecoder;
24+
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
25+
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
26+
import org.springframework.util.Assert;
27+
import org.springframework.util.StringUtils;
28+
29+
import java.util.Map;
30+
import java.util.concurrent.ConcurrentHashMap;
31+
import java.util.function.Function;
32+
33+
import static org.springframework.security.oauth2.jwt.JwtProcessors.withJwkSetUri;
34+
35+
/**
36+
* Provides a default or custom implementation for {@link OAuth2TokenValidator}
37+
*
38+
* @author Joe Grandja
39+
* @author Rafael Dominguez
40+
* @since 5.2
41+
*
42+
* @see OAuth2TokenValidator
43+
* @see Jwt
44+
*/
45+
public final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<ClientRegistration> {
46+
private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
47+
private final Map<String, JwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
48+
private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = OidcIdTokenValidator::new;
49+
50+
@Override
51+
public JwtDecoder createDecoder(ClientRegistration clientRegistration) {
52+
Assert.notNull(clientRegistration, "clientRegistration cannot be null.");
53+
return this.jwtDecoders.computeIfAbsent(clientRegistration.getRegistrationId(), key -> {
54+
if (!StringUtils.hasText(clientRegistration.getProviderDetails().getJwkSetUri())) {
55+
OAuth2Error oauth2Error = new OAuth2Error(
56+
MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
57+
"Failed to find a Signature Verifier for Client Registration: '" +
58+
clientRegistration.getRegistrationId() +
59+
"'. Check to ensure you have configured the JwkSet URI.",
60+
null
61+
);
62+
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
63+
}
64+
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
65+
NimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder(withJwkSetUri(jwkSetUri).build());
66+
OAuth2TokenValidator<Jwt> jwtValidator = jwtValidatorFactory.apply(clientRegistration);
67+
jwtDecoder.setJwtValidator(jwtValidator);
68+
return jwtDecoder;
69+
});
70+
}
71+
72+
/**
73+
* Allows user customization for the {@link OAuth2TokenValidator}
74+
*
75+
* @param jwtValidatorFactory
76+
*/
77+
public final void setJwtValidatorFactory(Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory) {
78+
Assert.notNull(jwtValidatorFactory, "jwtValidatorFactory cannot be null.");
79+
this.jwtValidatorFactory = jwtValidatorFactory;
80+
}
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2002-2019 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+
* http://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.client.oidc.authentication;
17+
18+
import org.springframework.security.oauth2.client.registration.ClientRegistration;
19+
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
20+
import org.springframework.security.oauth2.core.OAuth2Error;
21+
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
22+
import org.springframework.security.oauth2.jwt.Jwt;
23+
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
24+
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
25+
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;
26+
import org.springframework.util.Assert;
27+
import org.springframework.util.StringUtils;
28+
29+
import java.util.Map;
30+
import java.util.concurrent.ConcurrentHashMap;
31+
import java.util.function.Function;
32+
33+
/**
34+
* Provides a default or custom reactive implementation for {@link OAuth2TokenValidator}
35+
*
36+
* @author Joe Grandja
37+
* @author Rafael Dominguez
38+
* @since 5.2
39+
*
40+
* @see OAuth2TokenValidator
41+
* @see Jwt
42+
*/
43+
public final class ReactiveOidcIdTokenDecoderFactory implements ReactiveJwtDecoderFactory<ClientRegistration> {
44+
private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
45+
private final Map<String, ReactiveJwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
46+
private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = OidcIdTokenValidator::new;
47+
48+
@Override
49+
public ReactiveJwtDecoder createDecoder(ClientRegistration clientRegistration) {
50+
Assert.notNull(clientRegistration, "clientRegistration cannot be null.");
51+
return this.jwtDecoders.computeIfAbsent(clientRegistration.getRegistrationId(), key -> {
52+
if (!StringUtils.hasText(clientRegistration.getProviderDetails().getJwkSetUri())) {
53+
OAuth2Error oauth2Error = new OAuth2Error(
54+
MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
55+
"Failed to find a Signature Verifier for Client Registration: '" +
56+
clientRegistration.getRegistrationId() +
57+
"'. Check to ensure you have configured the JwkSet URI.",
58+
null
59+
);
60+
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
61+
}
62+
NimbusReactiveJwtDecoder jwtDecoder = new NimbusReactiveJwtDecoder(
63+
clientRegistration.getProviderDetails().getJwkSetUri());
64+
OAuth2TokenValidator<Jwt> jwtValidator = jwtValidatorFactory.apply(clientRegistration);
65+
jwtDecoder.setJwtValidator(jwtValidator);
66+
return jwtDecoder;
67+
});
68+
}
69+
70+
/**
71+
* Allows user customization for the {@link OAuth2TokenValidator}
72+
*
73+
* @param jwtValidatorFactory
74+
*/
75+
public final void setJwtValidatorFactory(Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory) {
76+
Assert.notNull(jwtValidatorFactory, "jwtValidatorFactory cannot be null.");
77+
this.jwtValidatorFactory = jwtValidatorFactory;
78+
}
79+
}

0 commit comments

Comments
 (0)