{"record":{"id":"beae742553a98180","repo":"quarkusio/quarkus","slug":"jwt-svid-aud-claim-contains-unexpected-extra-aud","errorCode":null,"errorMessage":"JWT-SVID 'aud' claim contains unexpected extra audiences; requested: ${requestedAudiences}, received: ${audience}","messagePattern":"JWT-SVID 'aud' claim contains unexpected extra audiences; requested: (.+?), received: (.+?)","errorType":"exception","errorClass":"SpiffeConnectionException","httpStatus":null,"severity":"error","filePath":"extensions/spiffe-client/runtime/src/main/java/io/quarkus/spiffe/client/runtime/internal/SpiffeClientImpl.java","lineNumber":262,"sourceCode":"                    audience.add(audienceAsString);\n                } else {\n                    throw new SpiffeConnectionException(\n                            \"JWT-SVID 'aud' array element at index \" + i + \" is not a string:\" + audienceAsArray.getValue(i));\n                }\n            }\n        } else if (aud instanceof String audienceAsString) {\n            audience = Set.of(audienceAsString);\n        } else {\n            throw new SpiffeConnectionException(\n                    \"JWT-SVID 'aud' claim is not a string or array of strings\");\n        }\n        if (!audience.containsAll(requestedAudiences)) {\n            throw new SpiffeConnectionException(\n                    \"JWT-SVID 'aud' claim does not contain the requested audiences; requested: \"\n                            + requestedAudiences + \", received: \" + audience);\n        }\n        if (audience.size() != requestedAudiences.size()) {\n            throw new SpiffeConnectionException(\n                    \"JWT-SVID 'aud' claim contains unexpected extra audiences; requested: \"\n                            + requestedAudiences + \", received: \" + audience);\n        }\n\n        Long exp = payload.getLong(\"exp\");\n        if (exp == null) {\n            throw new SpiffeConnectionException(\"JWT-SVID from SPIRE agent is missing the required 'exp' claim\");\n        }\n        Instant expiry = Instant.ofEpochSecond(exp);\n        if (expiry.isBefore(Instant.now())) {\n            throw new SpiffeConnectionException(\"JWT-SVID from SPIRE agent is already expired\");\n        }\n\n        record WorkloadJsonWebTokenImpl(String token, String subject, Set<String> audience,\n                Instant expiry) implements WorkloadJsonWebToken {\n        }\n        return new WorkloadJsonWebTokenImpl(token, sub, Set.copyOf(audience), expiry);\n    }","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/spiffe-client/runtime/src/main/java/io/quarkus/spiffe/client/runtime/internal/SpiffeClientImpl.java#L244-L280","documentation":"If the token's aud set strictly contains all requested audiences but also has extra entries, the client treats the token as over-scoped and rejects it (audience.size() != requestedAudiences.size()). This enforces exact audience matching between request and issued JWT-SVID.","triggerScenarios":"SPIRE entry issues tokens with multiple audiences while the client requested only a subset, e.g. requesting {\"a\"} but receiving aud [\"a\",\"b\"].","commonSituations":"Broad SPIRE registration entries granting many audiences; shared workload entries reused by several services; tightening audiences in app config without narrowing the SPIRE entry (or vice versa).","solutions":["Narrow the SPIRE registration entry so issued tokens contain exactly the required audiences","Request the full audience set that the entry grants, or update config to match the token contents","Create separate SPIRE entries per service pairing to keep scopes minimal"],"exampleFix":"// before (entry grants a+b, request asks only a)\nspiffeClient.getWorkloadJsonWebToken(Set.of(\"a\"));\n// after\nspiffeClient.getWorkloadJsonWebToken(Set.of(\"a\", \"b\")); // or narrow the SPIRE entry","handlingStrategy":"validation","validationCode":"// request exactly the audience set the SPIRE entry grants\nSet<String> entryGranted = Set.of(\"https://api.example.com\");\n// call with the full set, or narrow the entry with spire-server entry update","typeGuard":null,"tryCatchPattern":"try {\n    return spiffeClient.getWorkloadJsonWebToken(requested).await().indefinitely();\n} catch (SpiffeConnectionException e) {\n    if (e.getMessage().contains(\"unexpected extra audiences\")) {\n        // narrow the SPIRE entry or request all granted audiences\n    }\n    throw e;\n}","preventionTips":["Prefer one SPIRE entry per service pairing with minimal audiences","Review entries after adding new audiences to shared workloads","Document the exact audience contract between services"],"tags":["spiffe","jwt","aud-claim","audience-mismatch"],"backgroundTag":"jwt-audience-mismatch","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}