apereo/cas · error

Service [ ] with client id [ ] is configured to encrypt…

Error message

Service [{}] with client id [{}] is configured to encrypt tokens, yet no JSON web key is available

What it means

This warning is logged by OidcJsonWebKeySetUtils.fetchJsonWebKeySetForEncryption (OidcJsonWebKeyStoreUtils.java:276) when a service is flagged for token encryption but the registered-service JWKS cache contains no entry for ENCRYPTION usage; the method returns Optional.empty() so the token cannot be encrypted with the client's key.

Solutions

  1. Ensure the service's JWKS contains keys with use=enc (encryption) and that the jwks property on the OidcRegisteredService points to a reachable, valid JWKS resource
  2. If the service should not encrypt tokens, disable the token-encryption setting on the OidcRegisteredService
  3. Verify the remote JWKS endpoint (e.g. the client's jwks_uri) is up and returns 2xx; fix networking/auth and reload the service registry
  4. Clear and rebuild the registered-service JWKS cache so encryption keys are re-fetched after fixing the source

Example fix

// before: service flags encryption but only signing keys exist
"@class":"org.apereo.cas.services.OidcRegisteredService",
"jwks":"https://rp.example.com/jwks.json" // keys all have use=sign
// after: publish an enc key in the RP JWKS (or disable encryption)
// JWKS at https://rp.example.com/jwks.json now includes:
{"kty":"RSA","use":"enc","kid":"rp-enc-1","n":"...","e":"AQAB"}
Defensive patterns

Strategy: validation

Validate before calling

// Before enabling token encryption for a service, verify enc keys exist in its JWKS
JsonWebKeySet jwks = new JsonWebKeySet(fetchJwks(service.getJwks()));
boolean hasEncKey = jwks.getJsonWebKeys().stream()
    .anyMatch(k -> "enc".equals(k.getUse()) && k.getKey() != null);
if (!hasEncKey) {
    throw new IllegalStateException("Service " + service.getClientId()
        + " requires encryption but its JWKS has no use=enc key");
}

Type guard

static boolean serviceHasEncryptionKeys(OidcRegisteredService service, JsonWebKeySet jwks) {
    return jwks.getJsonWebKeys().stream()
        .anyMatch(k -> OidcJsonWebKeyUsage.ENCRYPTION.is(k) && k.getKey() != null);
}

Try / catch

try {
    Optional<JsonWebKeySet> keys =
        OidcJsonWebKeyStoreUtils.fetchJsonWebKeySetForEncryption(service, cipherExecutor);
    if (keys.isEmpty()) {
        LOG.warn("Skipping token encryption for client {} - no enc JWKS", service.getClientId());
    }
} catch (Exception e) {
    LOG.error("Encryption JWKS fetch failed for {}", service.getClientId(), e);
}

Prevention

When it happens

Trigger: fetchJsonWebKeySetForEncryption is called for an OidcRegisteredService that requires encrypted ID tokens/tokens, but cipherExecutor.getRegisteredServiceJsonWebKeystoreCache().get(new OidcJsonWebKeyCacheKey(service, ENCRYPTION)) returns an empty Optional — i.e. no encryption key was ever loaded/created for that service's JWKS.

Common situations: Service has token encryption enabled but its jwks URL/keystore contains only signing keys (no use=enc keys); the remote JWKS endpoint is unreachable so the cache never populated; the service's jwks property is blank or points to a stale file; after cache invalidation the encryption keys were not re-fetched.

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


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/a9d9277a178563d2. 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:276

        }
        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());
            return Optional.empty();
        }
        val jsonWebKey = jwks.get();
        LOGGER.debug("Found JSON web key to encrypt the token: [{}]", jsonWebKey);

        val keys = jsonWebKey.getJsonWebKeys().stream().filter(key -> key.getKey() != null).toList();
        if (keys.isEmpty()) {
            LOGGER.warn("No valid JSON web keys used for encryption can be found");
            return Optional.empty();
        }
        return Optional.of(new JsonWebKeySet(keys));
    }
}

View on GitHub (pinned to e7288fc434)