{"record":{"id":"d99d9eff5e0b1916","repo":"prestodb/presto","slug":"error-while-fetching-access-token","errorCode":null,"errorMessage":"Error while fetching access token: ","messagePattern":"Error while fetching access token: ","errorType":"http","errorClass":"ChallengeFailedException","httpStatus":401,"severity":"error","filePath":"presto-main/src/main/java/com/facebook/presto/server/security/oauth2/NimbusOAuth2Client.java","lineNumber":454,"sourceCode":"\n    private <T extends AccessTokenResponse> T getTokenResponse(String code, URI callbackUri, NimbusAirliftHttpClient.Parser<T> parser)\n            throws ChallengeFailedException\n    {\n        return getTokenResponse(new AuthorizationCodeGrant(new AuthorizationCode(code), callbackUri), parser);\n    }\n\n    private <T extends AccessTokenResponse> T getTokenResponse(String refreshToken, NimbusAirliftHttpClient.Parser<T> parser)\n            throws ChallengeFailedException\n    {\n        return getTokenResponse(new RefreshTokenGrant(new RefreshToken(refreshToken)), parser);\n    }\n\n    private <T extends AccessTokenResponse> T getTokenResponse(AuthorizationGrant authorizationGrant, NimbusAirliftHttpClient.Parser<T> parser)\n            throws ChallengeFailedException\n    {\n        T tokenResponse = httpClient.execute(new TokenRequest(tokenUrl, clientAuth, authorizationGrant, scope), parser);\n        if (!tokenResponse.indicatesSuccess()) {\n            throw new ChallengeFailedException(\"Error while fetching access token: \" + tokenResponse.toErrorResponse().toJSONObject());\n        }\n        return tokenResponse;\n    }\n\n    /**\n     * Retrieves JWT claims for the given access token.\n     *\n     * IMPORTANT: This method should NOT be used for extracting the principal field.\n     * Per OIDC specification, the principal should come from the ID token, not the access token.\n     * This method is kept for backward compatibility and the getClaims() API method.\n     *\n     * @param accessToken the access token value\n     * @return Optional containing claims from access token or UserInfo endpoint\n     */\n    private Optional<JWTClaimsSet> getJWTClaimsSet(String accessToken)\n    {\n        // Try parsing access token as JWT\n        Optional<JWTClaimsSet> claims = parseAccessToken(accessToken);","sourceCodeStart":436,"sourceCodeEnd":472,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-main/src/main/java/com/facebook/presto/server/security/oauth2/NimbusOAuth2Client.java#L436-L472","documentation":"Thrown by NimbusOAuth2Client when the HTTP exchange to the OAuth2 token endpoint fails while exchanging an authorization code or refresh token; the exception detail is appended. It means the server's token could not be obtained, so the login challenge fails rather than the request being malformed.","triggerScenarios":"Exchanging the authorization code when the code is expired/already used, redirect_uri or client credentials mismatch the original request, or the IdP rejects the grant (invalid_grant, unauthorized_client).","commonSituations":"Replaying an old callback URL after login already completed, mismatched oauth2.client-id/secret or redirect-uri config, clock skew, callback retried by the browser, load balancer double-delivering the callback.","solutions":["Start a fresh login flow instead of replaying the old callback URL (codes are single-use)","Verify oauth2.client-id, client-secret and redirect-uri match the IdP application registration exactly","Check the appended IdP error JSON in the message (e.g. invalid_grant) to identify the exact rejection","Ensure the callback is not retried/double-delivered by proxies or browser refresh"],"exampleFix":"// before\n// browser refresh of POST /oauth2/callback?code=OLD_CODE -> invalid_grant\n// after\n// redirect user to a fresh /oauth2/authentication/challenge flow","handlingStrategy":"retry","validationCode":"// Verify oauth2.client-id, client-secret, redirect-uri are non-empty and match the IdP app registration before starting the flow","typeGuard":"boolean tokenRequestConfigValid(String clientId, String secret, java.net.URI redirectUri) { return clientId != null && !clientId.isEmpty() && secret != null && !secret.isEmpty() && redirectUri != null; }","tryCatchPattern":"try { return client.getOAuth2Response(code, callbackUri, nonce); } catch (ChallengeFailedException e) { if (!e.getMessage().contains(\"invalid_grant\")) retryWithBackoff(); else restartLoginFlow(); throw e; }","preventionTips":["Never replay authorization codes; always start a fresh challenge after any token-endpoint error","Match redirect-uri byte-for-byte with the IdP registration","Alert on the embedded IdP error JSON in the message to classify invalid_grant vs unauthorized_client","Prevent proxies from double-delivering the OAuth2 callback"],"tags":["oauth2","token-endpoint","http-error"],"backgroundTag":"oauth-token-exchange-failed","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"}