apereo/cas · error
No [ ] key could be found for issuer [ ]
Error message
No [{}] key could be found for issuer [{}] What it means
This warning is logged by OidcJsonWebKeyStoreUtils.fetchJsonWebKeySetForSigning when no signing JWKS is available: the per-service signing keystore cache entry is absent, and (when fallbackToDefault is true) the default global signing keystore cache for the determined issuer is also empty or null. The method returns Optional.empty(), so token signing cannot proceed with a JWKS.
Solutions
- Ensure the default OIDC JWKS keystore is generated and reachable: verify cas.authn.oidc.jwks.jwks-file (or REST endpoint) points to a valid, writable/readable location and that the JWKS generation service ran
- Configure a jwks (and jwksKeyId) on the OidcRegisteredService so the per-service signing cache is populated
- If using the REST JWKS generator, confirm the endpoint returns a 2xx JWKS payload and that basic auth credentials are correct
- Check that the issuer URL used to build the default cache key matches the configured OIDC issuer; a mismatch causes cache lookups to miss
Example fix
# before: no keystore configured cas.authn.oidc.jwks.jwks-file= # after cas.authn.oidc.jwks.jwks-file=/etc/cas/config/oidc-signing-jwks.json # and ensure the file exists / is generated at startup
Defensive patterns
Strategy: fallback
Validate before calling
// Before signing, check a signing key set is resolvable
var cacheKey = new OidcJsonWebKeyCacheKey(issuer, OidcJsonWebKeyUsage.SIGNING);
boolean hasDefaultKeys = cipherExecutor.getDefaultJsonWebKeystoreCache().get(cacheKey) != null;
if (!hasDefaultKeys) {
LOG.warn("No default signing JWKS for issuer {} - trigger JWKS generation first", issuer);
} Type guard
static boolean hasSigningKeys(OidcRegisteredServiceJwtCipherExecutor cipherExecutor,
OidcRegisteredService service) {
return Objects.requireNonNull(
cipherExecutor.getRegisteredServiceJsonWebKeystoreCache()
.get(new OidcJsonWebKeyCacheKey(service, OidcJsonWebKeyUsage.SIGNING)))
.isPresent();
} Try / catch
try {
Optional<JsonWebKeySet> keys =
OidcJsonWebKeyStoreUtils.fetchJsonWebKeySetForSigning(service, cipherExecutor, true);
if (keys.isEmpty()) {
LOG.warn("No signing JWKS for issuer {}; refusing to issue signed token", issuer);
}
} catch (Exception e) {
LOG.error("Signing JWKS fetch failed", e);
} Prevention
- Configure cas.authn.oidc.jwks.jwks-file (or a reliable REST endpoint) so the default keystore is generated/persisted on startup
- Mount the JWKS file on persistent storage so restarts do not lose it
- Keep the configured OIDC issuer URL stable; cache keys are issuer-based
- Monitor the JWKS cache/generation on startup and alert when empty
When it happens
Trigger: Calling fetchJsonWebKeySetForSigning for a RegisteredService whose registered-service JWKS cache (keyed by service + SIGNING usage) has no entry, and whose issuer's default JWKS keystore cache entry is also missing — typically when no JWKS has been generated/loaded yet or the cache was just evicted/emptied at startup.
Common situations: CAS started before the OIDC JWKS generator produced/persisted the default keystore (e.g. JWKS file path unwritable or REST endpoint down); the relying-party service has no jwks configured and the global cas.authn.oidc.jwks keystore was never generated; cache expiry/eviction cleared entries before regeneration.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- Service [ ] with client id [ ] is configured to encrypt…
- JWKS cannot contain expressions
- Unable to use 'none' as introspection signing algorithm
- Unable to use 'none' for the user-info signing algorithm
- Service with client id is configured to encrypt tokens, yet…
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/794954c90040374b.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/jwks/OidcJsonWebKeyStoreUtils.java:259
val issuer = cipherExecutor.getOidcIssuerService().determineIssuer(Optional.of(oidcRegisteredService));
LOGGER.trace("Using issuer [{}] to determine JWKS from default keystore cache", issuer);
val serviceJsonWebKeys = Objects.requireNonNull(cipherExecutor.getRegisteredServiceJsonWebKeystoreCache().get(
new OidcJsonWebKeyCacheKey(oidcRegisteredService, OidcJsonWebKeyUsage.SIGNING)));
if (serviceJsonWebKeys.isPresent()) {
val jsonWebKey = serviceJsonWebKeys.get();
LOGGER.debug("Found JSON web key to sign the token: [{}]", jsonWebKey);
val keys = jsonWebKey.getJsonWebKeys().stream()
.filter(key -> key.getKey() != null).collect(Collectors.toList());
return Optional.of(new JsonWebKeySet(keys));
}
if (fallbackToDefault) {
val cacheKey = new OidcJsonWebKeyCacheKey(issuer, OidcJsonWebKeyUsage.SIGNING);
val defaultJsonWebKeys = cipherExecutor.getDefaultJsonWebKeystoreCache().get(cacheKey);
if (defaultJsonWebKeys != null) {
return Optional.of(defaultJsonWebKeys);
}
}
LOGGER.warn("No [{}] key could be found for issuer [{}]", OidcJsonWebKeyUsage.SIGNING, issuer);
return Optional.empty();
}
/**
* Fetch json web key set for encryption.
*
* @param registeredService the registered service
* @param cipherExecutor the cipher executor
* @return the optional
*/
public static Optional<JsonWebKeySet> fetchJsonWebKeySetForEncryption(final RegisteredService registeredService,
final OidcRegisteredServiceJwtCipherExecutor cipherExecutor) {
val oidcRegisteredService = (OidcRegisteredService) registeredService;
val jwks = Objects.requireNonNull(cipherExecutor.getRegisteredServiceJsonWebKeystoreCache().get(
new OidcJsonWebKeyCacheKey(oidcRegisteredService, OidcJsonWebKeyUsage.ENCRYPTION)));
if (jwks.isEmpty()) {
LOGGER.warn("Service [{}] with client id [{}] is configured to encrypt tokens, yet no JSON web key is available",
oidcRegisteredService.getServiceId(), oidcRegisteredService.getClientId());View on GitHub (pinned to e7288fc434)