{"record":{"id":"ca1de12d3554da2a","repo":"prestodb/presto","slug":"userinfo-endpoint-returned-error","errorCode":null,"errorMessage":"UserInfo endpoint returned error: ","messagePattern":"UserInfo endpoint returned error: ","errorType":"http","errorClass":"RuntimeException","httpStatus":401,"severity":"error","filePath":"presto-main/src/main/java/com/facebook/presto/server/security/oauth2/NimbusOAuth2Client.java","lineNumber":576,"sourceCode":"\n    /**\n     * Fetches user information claims from the UserInfo endpoint.\n     *\n     * @param accessToken the OAuth2 access token for authentication\n     * @return JWTClaimsSet containing user information\n     * @throws ParseException if the response cannot be parsed\n     * @throws RuntimeException if the HTTP request fails\n     */\n    private JWTClaimsSet fetchUserInfoClaims(String accessToken) throws ParseException\n    {\n        UserInfoResponse response = httpClient.execute(\n                new UserInfoRequest(userinfoUrl.get(), new BearerAccessToken(accessToken)),\n                this::parse);\n\n        if (!response.indicatesSuccess()) {\n            UserInfoErrorResponse errorResponse = response.toErrorResponse();\n            LOG.error(\"Received error from UserInfo endpoint: %s\", errorResponse.getErrorObject());\n            throw new RuntimeException(\"UserInfo endpoint returned error: \" + errorResponse.getErrorObject());\n        }\n\n        return response.toSuccessResponse().getUserInfo().toJWTClaimsSet();\n    }\n\n    // Using this parsing method for our /userinfo response from the IdP in order to allow for different principal\n    // fields as defined, and in the absence of the `sub` claim. This is a \"hack\" solution to alter the claims\n    // present in the response before calling the parser provided by the oidc sdk, which fails hard if the\n    // `sub` claim is missing.\n    public UserInfoResponse parse(HTTPResponse httpResponse)\n            throws ParseException\n    {\n        // Check status code first and only process payload if successful\n        if (httpResponse.getStatusCode() != 200) {\n            return UserInfoErrorResponse.parse(httpResponse);\n        }\n\n        JSONObject body = httpResponse.getBodyAsJSONObject();","sourceCodeStart":558,"sourceCodeEnd":594,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-main/src/main/java/com/facebook/presto/server/security/oauth2/NimbusOAuth2Client.java#L558-L594","documentation":"NimbusOAuth2Client.fetchUserInfoClaims got an error response from the OAuth2 UserInfo endpoint (non-success HTTP status); the endpoint's error body is appended. The claims fetch needed to establish the authenticated principal failed.","triggerScenarios":"GET /userinfo with a bearer token the IdP rejects: access token expired or revoked, wrong audience/scope, token issued for a different IdP instance, or the userinfo endpoint is down/misconfigured.","commonSituations":"Clock skew after authentication making the token appear expired, oauth2.access-token audiences config missing the required audience, IdP changed userinfo requirements, network/proxy stripping the Authorization header.","solutions":["Read the logged 'Received error from UserInfo endpoint' error object (e.g. invalid_token, insufficient_scope) to identify the cause","Verify oauth2.access-token.audiences includes the audience the IdP puts on access tokens","Re-authenticate to obtain a fresh access token if expired or revoked","Confirm the proxy/load balancer forwards the Authorization: Bearer header to /userinfo"],"exampleFix":"// before\n// oauth2.access-token.audiences not set; IdP requires audience=coordinator\n// after\n<property name=\"oauth2.access-token.audiences\">coordinator</property>","handlingStrategy":"try-catch","validationCode":"// Pre-flight: curl -H \"Authorization: Bearer <token>\" https://idp/userinfo to confirm the token is accepted before wiring the flow","typeGuard":"boolean userInfoResponseOk(int status) { return status >= 200 && status < 300; }","tryCatchPattern":"try { return client.getOAuth2Response(code, callbackUri, nonce); } catch (RuntimeException e) { if (e.getMessage().startsWith(\"UserInfo endpoint returned error\")) { LOG.warn(\"UserInfo rejected token: {}\", e.getMessage()); reauthenticate(); } throw e; }","preventionTips":["Configure oauth2.access-token.audiences to include the audience the IdP stamps on access tokens","Request the scopes the userinfo endpoint requires (openid, profile, email as needed)","Monitor the coordinator log for the 'Received error from UserInfo endpoint' entry to catch IdP-side issues early","Verify proxies forward the Authorization header to /userinfo"],"tags":["oauth2","userinfo","http-error"],"backgroundTag":"userinfo-endpoint-error","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"}