apereo/cas · warning
Unable to successfully fetch JWKS resource from
Error message
Unable to successfully fetch JWKS resource from [{}] What it means
This warning is logged by OidcRestfulJsonWebKeystoreGeneratorService.generate when the HTTP GET to the configured cas.authn.oidc.jwks.rest.url fails: HttpUtils.execute returns null (connection failure, timeout, exhausted retries) or the response status is not 2xx. The method returns null, meaning no JWKS resource could be fetched from the REST endpoint.
Solutions
- Verify cas.authn.oidc.jwks.rest.url is correct and the endpoint is reachable from the CAS server (curl the URL and expect a 2xx JWKS body)
- Check/fix the basic auth username and password configured for the REST JWKS endpoint
- Inspect the endpoint's server logs for the failing status (404/401/500) and fix the server-side cause
- If the endpoint is intermittently unavailable, raise maximumRetryAttempts or add a fallback JWKS file configuration
Example fix
# before: wrong credentials/URL cas.authn.oidc.jwks.rest.url=https://jwks.internal.example.com/wrong-path cas.authn.oidc.jwks.rest.basic-auth-username=stale-user # after cas.authn.oidc.jwks.rest.url=https://jwks.internal.example.com/jwks cas.authn.oidc.jwks.rest.basic-auth-username=svc-cas cas.authn.oidc.jwks.rest.basic-auth-password=<current-secret>
Defensive patterns
Strategy: retry
Validate before calling
// Before configuring the REST JWKS source, verify it serves a 2xx JWKS
HttpRequest request = HttpRequest.newBuilder(URI.create(restUrl)).GET().build();
HttpResponse<String> resp = client.send(request, HttpResponse.BodyHandlers.ofString());
if (resp.statusCode() / 100 != 2) {
throw new IllegalStateException("JWKS REST endpoint " + restUrl
+ " returned HTTP " + resp.statusCode());
} Type guard
static boolean isReachableJwksEndpoint(String url) {
try {
var exec = HttpExecutionRequest.builder().method(HttpMethod.GET).url(url).build();
var response = HttpUtils.execute(exec);
return response != null && HttpStatus.valueOf(response.getCode()).is2xxSuccessful();
} catch (Exception e) {
return false;
}
} Try / catch
try {
Optional<Resource> jwks = generatorService.find();
if (jwks.isEmpty()) {
LOG.warn("REST JWKS source unavailable; falling back to file-based JWKS");
return fileBasedJwksResource();
}
return jwks;
} catch (Exception e) {
LOG.error("JWKS fetch from REST failed", e);
return Optional.empty();
} Prevention
- Health-check the JWKS REST endpoint before and during CAS startup; delay JWKS loading until it is up
- Verify basic-auth credentials and URL with curl before wiring them into cas.authn.oidc.jwks.rest.*
- Increase cas.authn.oidc.jwks.rest.maximum-retry-attempts for flaky networks
- Keep a file-based JWKS fallback so OIDC survives transient REST outages
- Ensure TLS trust for the endpoint's certificate is present in the CAS JVM truststore
When it happens
Trigger: generate()/find() is invoked while the REST JWKS endpoint is down or unreachable, returns 401/403 due to wrong basic-auth credentials (basicAuthUsername/basicAuthPassword), returns 404/500, or the configured URL is wrong; retries configured via maximumRetryAttempts are exhausted.
Common situations: External JWKS store service not yet started when CAS boots; misconfigured cas.authn.oidc.jwks.rest.url (typo, wrong host/port, TLS certificate not trusted); incorrect basic auth credentials; firewall/proxy blocking the request; endpoint returning 401 after a credential rotation.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- Failed to save google authenticator account
- Unable to accept response status
- <policy status exception>
- No credentials can be extracted to authenticate the REST…
- Unable to get entity from MDQ server and a backup file does…
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/2933955ff3c0b41c.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/jwks/generator/OidcRestfulJsonWebKeystoreGeneratorService.java:56
@Override
public Optional<Resource> find() throws Exception {
return Optional.ofNullable(generate());
}
@Override
public @Nullable Resource generate() throws Exception {
val rest = oidcProperties.getJwks().getRest();
val exec = HttpExecutionRequest.builder()
.basicAuthPassword(rest.getBasicAuthPassword())
.basicAuthUsername(rest.getBasicAuthUsername())
.maximumRetryAttempts(rest.getMaximumRetryAttempts())
.method(HttpMethod.GET)
.headers(rest.getHeaders())
.url(rest.getUrl())
.build();
val response = HttpUtils.execute(exec);
if (response == null || !HttpStatus.valueOf(response.getCode()).is2xxSuccessful()) {
LOGGER.warn("Unable to successfully fetch JWKS resource from [{}]", rest.getUrl());
return null;
}
try (val content = ((HttpEntityContainer) response).getEntity().getContent()) {
val result = IOUtils.toString(content, StandardCharsets.UTF_8);
LOGGER.debug("Received payload result from [{}] as [{}]", rest.getUrl(), result);
return new ByteArrayResource(result.getBytes(StandardCharsets.UTF_8), "OIDC JWKS");
}
}
@Override
public JsonWebKeySet store(final JsonWebKeySet jsonWebKeySet) {
val rest = oidcProperties.getJwks().getRest();
val headers = CollectionUtils.<String, String>wrap(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
headers.putAll(rest.getHeaders());
val exec = HttpExecutionRequest.builder()
.basicAuthPassword(rest.getBasicAuthPassword())
.basicAuthUsername(rest.getBasicAuthUsername())View on GitHub (pinned to e7288fc434)