{"record":{"id":"794954c90040374b","repo":"apereo/cas","slug":"no-key-could-be-found-for-issuer","errorCode":null,"errorMessage":"No [{}] key could be found for issuer [{}]","messagePattern":"No \\[(.+?)\\] key could be found for issuer \\[(.+?)\\]","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/jwks/OidcJsonWebKeyStoreUtils.java","lineNumber":259,"sourceCode":"        val issuer = cipherExecutor.getOidcIssuerService().determineIssuer(Optional.of(oidcRegisteredService));\n        LOGGER.trace(\"Using issuer [{}] to determine JWKS from default keystore cache\", issuer);\n        val serviceJsonWebKeys = Objects.requireNonNull(cipherExecutor.getRegisteredServiceJsonWebKeystoreCache().get(\n            new OidcJsonWebKeyCacheKey(oidcRegisteredService, OidcJsonWebKeyUsage.SIGNING)));\n        if (serviceJsonWebKeys.isPresent()) {\n            val jsonWebKey = serviceJsonWebKeys.get();\n            LOGGER.debug(\"Found JSON web key to sign the token: [{}]\", jsonWebKey);\n            val keys = jsonWebKey.getJsonWebKeys().stream()\n                .filter(key -> key.getKey() != null).collect(Collectors.toList());\n            return Optional.of(new JsonWebKeySet(keys));\n        }\n        if (fallbackToDefault) {\n            val cacheKey = new OidcJsonWebKeyCacheKey(issuer, OidcJsonWebKeyUsage.SIGNING);\n            val defaultJsonWebKeys = cipherExecutor.getDefaultJsonWebKeystoreCache().get(cacheKey);\n            if (defaultJsonWebKeys != null) {\n                return Optional.of(defaultJsonWebKeys);\n            }\n        }\n        LOGGER.warn(\"No [{}] key could be found for issuer [{}]\", OidcJsonWebKeyUsage.SIGNING, issuer);\n        return Optional.empty();\n    }\n\n    /**\n     * Fetch json web key set for encryption.\n     *\n     * @param registeredService the registered service\n     * @param cipherExecutor    the cipher executor\n     * @return the optional\n     */\n    public static Optional<JsonWebKeySet> fetchJsonWebKeySetForEncryption(final RegisteredService registeredService,\n                                                                          final OidcRegisteredServiceJwtCipherExecutor cipherExecutor) {\n        val oidcRegisteredService = (OidcRegisteredService) registeredService;\n        val jwks = Objects.requireNonNull(cipherExecutor.getRegisteredServiceJsonWebKeystoreCache().get(\n            new OidcJsonWebKeyCacheKey(oidcRegisteredService, OidcJsonWebKeyUsage.ENCRYPTION)));\n        if (jwks.isEmpty()) {\n            LOGGER.warn(\"Service [{}] with client id [{}] is configured to encrypt tokens, yet no JSON web key is available\",\n                oidcRegisteredService.getServiceId(), oidcRegisteredService.getClientId());","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/jwks/OidcJsonWebKeyStoreUtils.java#L241-L277","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before: no keystore configured\ncas.authn.oidc.jwks.jwks-file=\n# after\ncas.authn.oidc.jwks.jwks-file=/etc/cas/config/oidc-signing-jwks.json\n# and ensure the file exists / is generated at startup","handlingStrategy":"fallback","validationCode":"// Before signing, check a signing key set is resolvable\nvar cacheKey = new OidcJsonWebKeyCacheKey(issuer, OidcJsonWebKeyUsage.SIGNING);\nboolean hasDefaultKeys = cipherExecutor.getDefaultJsonWebKeystoreCache().get(cacheKey) != null;\nif (!hasDefaultKeys) {\n    LOG.warn(\"No default signing JWKS for issuer {} - trigger JWKS generation first\", issuer);\n}","typeGuard":"static boolean hasSigningKeys(OidcRegisteredServiceJwtCipherExecutor cipherExecutor,\n                               OidcRegisteredService service) {\n    return Objects.requireNonNull(\n            cipherExecutor.getRegisteredServiceJsonWebKeystoreCache()\n                .get(new OidcJsonWebKeyCacheKey(service, OidcJsonWebKeyUsage.SIGNING)))\n        .isPresent();\n}","tryCatchPattern":"try {\n    Optional<JsonWebKeySet> keys =\n        OidcJsonWebKeyStoreUtils.fetchJsonWebKeySetForSigning(service, cipherExecutor, true);\n    if (keys.isEmpty()) {\n        LOG.warn(\"No signing JWKS for issuer {}; refusing to issue signed token\", issuer);\n    }\n} catch (Exception e) {\n    LOG.error(\"Signing JWKS fetch failed\", e);\n}","preventionTips":["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"],"tags":["oidc","jwks","signing","cache-miss"],"backgroundTag":"record-not-found","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}