{"record":{"id":"0fec40c31a0ecfee","repo":"prestodb/presto","slug":"unsupported-aud-claim-type-in-userinfo-response","errorCode":null,"errorMessage":"Unsupported 'aud' claim type in /userinfo response","messagePattern":"Unsupported 'aud' claim type in /userinfo response","errorType":"http","errorClass":"ParseException","httpStatus":401,"severity":"error","filePath":"presto-main/src/main/java/com/facebook/presto/server/security/oauth2/NimbusOAuth2Client.java","lineNumber":621,"sourceCode":"            httpResponse.setBody(body.toJSONString());\n        }\n\n        Object audClaim = body.get(\"aud\");\n        // only validate aud claim if it exists\n        if (audClaim != null) {\n            List<String> audiences;\n\n            if (audClaim instanceof String) {\n                audiences = List.of((String) audClaim);\n            }\n            else if (audClaim instanceof List<?>) {\n                audiences = ((List<?>) audClaim).stream()\n                        .filter(String.class::isInstance)\n                        .map(String.class::cast)\n                        .collect(toImmutableList());\n            }\n            else {\n                throw new ParseException(\"Unsupported 'aud' claim type in /userinfo response\");\n            }\n\n            if (!audiences.contains(clientId.getValue()) && Collections.disjoint(audiences, accessTokenAudiences)) {\n                throw new ParseException(\"Invalid audience in /userinfo response\");\n            }\n        }\n\n        return UserInfoSuccessResponse.parse(httpResponse);\n    }\n\n    private Optional<JWTClaimsSet> parseAccessToken(String accessToken)\n    {\n        try {\n            return Optional.of(accessTokenProcessor.process(accessToken, null));\n        }\n        catch (java.text.ParseException | BadJOSEException | JOSEException e) {\n            LOG.debug(e, \"Failed to parse JWT access token\");\n            return Optional.empty();","sourceCodeStart":603,"sourceCodeEnd":639,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-main/src/main/java/com/facebook/presto/server/security/oauth2/NimbusOAuth2Client.java#L603-L639","documentation":"The /userinfo parser validates the 'aud' claim: it must be a string or array of strings, and the audience must include the OAuth2 client id or one of the configured access-token audiences. A claim of an unexpected JSON type throws ParseException('Unsupported 'aud' claim type in /userinfo response').","triggerScenarios":"The IdP returns 'aud' as a nested array of non-strings, an object, a number, or another non-string/non-list JSON type in the /userinfo response.","commonSituations":"Non-standard or custom IdPs emitting unusual aud representations, IdP version changes altering claim serialization, misbehaving API gateways rewriting the userinfo body.","solutions":["Inspect the raw /userinfo response and confirm the aud claim type","Fix or upgrade the IdP so aud is a string or array of strings","If the IdP cannot be changed, configure audiences correctly or use an IdP that emits standard OIDC claims","Check for a proxy rewriting the userinfo JSON body"],"exampleFix":"// before\n{\"aud\": {\"client\": \"abc\"}}           // object, unsupported\n// after\n{\"aud\": [\"abc\"]}                      // array of strings, supported","handlingStrategy":"type-guard","validationCode":"// Pre-check the aud claim type before parsing\nObject aud = userInfoJson.opt(\"aud\");\nif (aud != null && !(aud instanceof String) && !(aud instanceof org.json.JSONArray)) { throw new IllegalStateException(\"Unsupported aud type in /userinfo response\"); }","typeGuard":"boolean isSupportedAudClaim(Object aud) { if (aud instanceof String) return true; if (aud instanceof org.json.JSONArray) { for (int i = 0; i < ((org.json.JSONArray) aud).length(); i++) { if (!(((org.json.JSONArray) aud).opt(i) instanceof String)) return false; } return true; } return false; }","tryCatchPattern":"try { return parseUserInfoResponse(httpResponse); } catch (com.nimbusds.oauth2.sdk.ParseException e) { if (e.getMessage().contains(\"aud\")) { LOG.error(\"IdP emitted non-standard aud claim: {}\", httpResponse.getBodyAsJSONObject().opt(\"aud\")); } throw e; }","preventionTips":["Verify the IdP emits aud as a string or array of strings during setup","Confirm oauth2.client-id and oauth2.access-token.audiences cover all audiences the IdP uses","Watch for IdP/gateway upgrades that alter userinfo JSON serialization","Log the raw aud value on parse failure to diagnose quickly"],"tags":["oauth2","userinfo","parse-error","audience-validation"],"backgroundTag":"invalid-audience-claim","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}