{"record":{"id":"c18bb96c85473fb5","repo":"spring-projects/spring-security","slug":"s","errorCode":null,"errorMessage":"%s","messagePattern":"%s","errorType":"exception","errorClass":"RemoteKeySourceException","httpStatus":null,"severity":"critical","filePath":"oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java","lineNumber":559,"sourceCode":"\t\t\t@Override\n\t\t\tpublic JWKSet getJWKSet(JWKSetCacheRefreshEvaluator refreshEvaluator, long currentTime, C context)\n\t\t\t\t\tthrows KeySourceException {\n\t\t\t\ttry {\n\t\t\t\t\tthis.reentrantLock.lock();\n\t\t\t\t\tif (refreshEvaluator.requiresRefresh(this.jwkSet)) {\n\t\t\t\t\t\tthis.cache.invalidate();\n\t\t\t\t\t}\n\t\t\t\t\tthis.cache.get(this.jwkSetUri, this::fetchJwks);\n\t\t\t\t\tAssert.notNull(this.jwkSet, \"JWK Set must not be null\");\n\t\t\t\t\treturn this.jwkSet;\n\t\t\t\t}\n\t\t\t\tcatch (Cache.ValueRetrievalException ex) {\n\t\t\t\t\tThrowable cause = ex.getCause();\n\t\t\t\t\tif (cause instanceof RemoteKeySourceException keys) {\n\t\t\t\t\t\tthrow keys;\n\t\t\t\t\t}\n\t\t\t\t\tif (cause != null) {\n\t\t\t\t\t\tthrow new RemoteKeySourceException(cause.getMessage(), cause);\n\t\t\t\t\t}\n\t\t\t\t\tthrow new RemoteKeySourceException(ex.getMessage(), null);\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tthis.reentrantLock.unlock();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void close() {\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**","sourceCodeStart":541,"sourceCodeEnd":577,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java#L541-L577","documentation":"Thrown by NimbusJwtDecoder's JWK Set source when refreshing the cached JWK Set from the authorization server's jwks-uri fails. The cache loader wraps the fetch failure in a Cache.ValueRetrievalException; if the underlying cause is any exception other than RemoteKeySourceException (e.g. a network/IO error, RestClientException, or JWKSet.parse failure), it is re-wrapped as a RemoteKeySourceException with the cause's message. JWT validation cannot proceed because the signing keys could not be retrieved.","triggerScenarios":"Calling NimbusJwtDecoder.decode() (or any validate/jwt path) where the decoder must fetch the JWK Set from jwkSetUri and the HTTP fetch or JWKSet.parse throws an unexpected exception (connection refused, DNS failure, TLS error, malformed JWKS JSON, non-2xx response) that surfaces as Cache.ValueRetrievalException with a non-RemoteKeySourceException cause.","commonSituations":"Authorization server JWKS endpoint is down or unreachable behind a firewall/proxy; wrong jwks-uri hostname; self-signed or expired TLS certificates; JWKS endpoint returning an error page/HTML instead of JSON; no network from container/K8s pod; timeouts caused by an unconfigured RestOperations.","solutions":["Verify the configured jwks-uri is correct and reachable: curl -v <jwks-uri> from the same host/network as the application.","Check the wrapped cause in the log (RemoteKeySourceException.getCause()) to see the real failure (UnknownHostException, ConnectException, SSLHandshakeException, ParseException) and fix that root cause.","If the JWKS endpoint returns non-JSON (proxy HTML error page, 4xx/5xx), fix the authorization server or intermediary and ensure Content-Type is application/json.","If caused by TLS, import the authorization server's certificate into the JVM truststore or fix certificate expiry.","If caused by slow responses/timeouts, configure the decoder's RestOperations (setRestOperations) with appropriate connect/read timeouts and connection pooling.","Once the endpoint is reachable, retry decoding; the cache will repopulate on the next request."],"exampleFix":"// before\nJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(\"https://auth.example.org/.well-known/jwks\")\n\t.build();\n// after\nNimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(\"https://auth.example.org/oauth2/jwks\")\n\t.restOperations(restTemplateWithTimeouts()) // timeout + error handling configured\n\t.build();","handlingStrategy":"try-catch","validationCode":"// Pre-check JWKS reachability before decoding\ntry (java.net.http.HttpClient client = java.net.http.HttpClient.newHttpClient()) {\n\tvar resp = client.send(java.net.http.HttpRequest.newBuilder(URI.create(jwksUri)).build(),\n\t\tjava.net.http.HttpResponse.BodyHandlers.ofString());\n\tif (resp.statusCode() != 200 || !resp.headers().firstValue(\"Content-Type\").orElse(\"\").contains(\"json\")) {\n\t\tthrow new IllegalStateException(\"JWKS endpoint not serving JSON: \" + resp.statusCode());\n\t}\n}","typeGuard":"static boolean isJwksFetchFailure(Exception ex) {\n\treturn ex instanceof org.springframework.security.oauth2.jwt.JwtException\n\t\t&& ex.getCause() instanceof org.springframework.security.oauth2.core.OAuth2KeyException\n\t\t\t|| ex.getMessage() != null && ex.getMessage().contains(\"Failed to match\")\n\t\t\t|| ex instanceof org.springframework.security.oauth2.jwt.JwtValidationException;\n}","tryCatchPattern":"try {\n\tJwt jwt = decoder.decode(token);\n} catch (org.springframework.security.oauth2.jwt.JwtException ex) {\n\tThrowable root = ex;\n\twhile (root.getCause() != null) root = root.getCause();\n\tlog.error(\"JWKS fetch failed: {}\", root.getMessage());\n\tthrow new AuthenticationServiceException(\"JWK Set unavailable\", ex);\n}","preventionTips":["curl the jwks-uri from the app's network before deploying","Configure a RestOperations with connect/read timeouts via withJwkSetUri(...).restOperations(...)","Monitor authorization server JWKS endpoint availability","Fix TLS trust (import certs) rather than disabling hostname verification"],"tags":["jwt","jwks","network","spring-security","oauth2"],"backgroundTag":"http-request-failed","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}