Skip to content

Commit 8c6ed1d

Browse files
committed
Consider using RetryTemplate for resolving provider configuration
Closes gh-18610 Signed-off-by: Evgeniy Cheban <mister.cheban@gmail.com>
1 parent 22a9858 commit 8c6ed1d

File tree

1 file changed

+61
-21
lines changed

1 file changed

+61
-21
lines changed

oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,19 @@
3333
import com.nimbusds.jose.proc.JWSVerificationKeySelector;
3434
import com.nimbusds.jose.proc.SecurityContext;
3535
import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
36+
import org.apache.commons.logging.Log;
37+
import org.apache.commons.logging.LogFactory;
3638

3739
import org.springframework.core.ParameterizedTypeReference;
40+
import org.springframework.core.retry.RetryException;
41+
import org.springframework.core.retry.RetryPolicy;
42+
import org.springframework.core.retry.RetryTemplate;
43+
import org.springframework.core.retry.Retryable;
3844
import org.springframework.http.RequestEntity;
3945
import org.springframework.http.ResponseEntity;
4046
import org.springframework.http.client.SimpleClientHttpRequestFactory;
4147
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
4248
import org.springframework.util.Assert;
43-
import org.springframework.web.client.HttpClientErrorException;
4449
import org.springframework.web.client.RestOperations;
4550
import org.springframework.web.client.RestTemplate;
4651
import org.springframework.web.util.UriComponents;
@@ -55,10 +60,13 @@
5560
*
5661
* @author Thomas Vitale
5762
* @author Rafiullah Hamedy
63+
* @author Evgeniy Cheban
5864
* @since 5.2
5965
*/
6066
final class JwtDecoderProviderConfigurationUtils {
6167

68+
private static final Log LOG = LogFactory.getLog(JwtDecoderProviderConfigurationUtils.class);
69+
6270
private static final String OIDC_METADATA_PATH = "/.well-known/openid-configuration";
6371

6472
private static final String OAUTH_METADATA_PATH = "/.well-known/oauth-authorization-server";
@@ -158,28 +166,26 @@ private static String getMetadataIssuer(Map<String, Object> configuration) {
158166
}
159167

160168
private static Map<String, Object> getConfiguration(String issuer, RestOperations rest, UriComponents... uris) {
161-
String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " + "\"" + issuer + "\"";
162-
for (UriComponents uri : uris) {
163-
try {
164-
RequestEntity<Void> request = RequestEntity.get(uri.toUriString()).build();
165-
ResponseEntity<Map<String, Object>> response = rest.exchange(request, STRING_OBJECT_MAP);
166-
Map<String, Object> configuration = response.getBody();
167-
Assert.notNull(configuration, "configuration must not be null");
168-
Assert.isTrue(configuration.get("jwks_uri") != null, "The public JWK set URI must not be null");
169-
return configuration;
170-
}
171-
catch (IllegalArgumentException ex) {
172-
throw ex;
173-
}
174-
catch (RuntimeException ex) {
175-
if (!(ex instanceof HttpClientErrorException
176-
&& ((HttpClientErrorException) ex).getStatusCode().is4xxClientError())) {
177-
throw new IllegalArgumentException(errorMessage, ex);
178-
}
179-
// else try another endpoint
169+
// @formatter:off
170+
RetryPolicy retryPolicy = RetryPolicy.builder()
171+
.excludes(IllegalArgumentException.class)
172+
.maxRetries(uris.length)
173+
.build();
174+
// @formatter:on
175+
RetryTemplate retryTemplate = new RetryTemplate(retryPolicy);
176+
try {
177+
return retryTemplate.execute(new RetryableConfiguration(rest, issuer, uris));
178+
}
179+
catch (RetryException ex) {
180+
Throwable lastException = ex.getLastException();
181+
LOG.error("""
182+
Unable to resolve the Configuration with the provided Issuer of "%s", after: %d attempts
183+
""".formatted(issuer, ex.getRetryCount()), lastException);
184+
if (lastException instanceof RuntimeException) {
185+
throw (RuntimeException) lastException;
180186
}
187+
throw new RuntimeException(lastException.getMessage(), lastException);
181188
}
182-
throw new IllegalArgumentException(errorMessage);
183189
}
184190

185191
static UriComponents oidc(String issuer) {
@@ -209,4 +215,38 @@ static UriComponents oauth(String issuer) {
209215
// @formatter:on
210216
}
211217

218+
private static final class RetryableConfiguration implements Retryable<Map<String, Object>> {
219+
220+
private int currentUriIndex = 0;
221+
222+
private final RestOperations rest;
223+
224+
private final String issuer;
225+
226+
private final UriComponents[] uris;
227+
228+
private RetryableConfiguration(RestOperations rest, String issuer, UriComponents[] uris) {
229+
this.rest = rest;
230+
this.issuer = issuer;
231+
this.uris = uris;
232+
}
233+
234+
@Override
235+
public Map<String, Object> execute() {
236+
if (this.currentUriIndex < this.uris.length) {
237+
UriComponents uri = this.uris[this.currentUriIndex++];
238+
RequestEntity<Void> request = RequestEntity.get(uri.toUriString()).build();
239+
ResponseEntity<Map<String, Object>> response = this.rest.exchange(request, STRING_OBJECT_MAP);
240+
Map<String, Object> configuration = response.getBody();
241+
Assert.notNull(configuration, "configuration must not be null");
242+
Assert.isTrue(configuration.get("jwks_uri") != null, "The public JWK set URI must not be null");
243+
return configuration;
244+
}
245+
throw new IllegalArgumentException("""
246+
Unable to resolve the Configuration with the provided Issuer of "%s"
247+
""".formatted(this.issuer));
248+
}
249+
250+
}
251+
212252
}

0 commit comments

Comments
 (0)