{"record":{"id":"01c16ae27beef32b","repo":"justauth/JustAuth","slug":"error-description","errorCode":null,"errorMessage":"${error_description}","messagePattern":"\\$\\{error_description\\}","errorType":"exception","errorClass":"AuthException","httpStatus":null,"severity":"error","filePath":"src/main/java/me/zhyd/oauth/request/AbstractAuthMicrosoftRequest.java","lineNumber":78,"sourceCode":"        this.checkResponse(accessTokenObject);\n\n        return AuthToken.builder()\n            .accessToken(accessTokenObject.getString(\"access_token\"))\n            .expireIn(accessTokenObject.getIntValue(\"expires_in\"))\n            .scope(accessTokenObject.getString(\"scope\"))\n            .tokenType(accessTokenObject.getString(\"token_type\"))\n            .refreshToken(accessTokenObject.getString(\"refresh_token\"))\n            .build();\n    }\n\n    /**\n     * 检查响应内容是否正确\n     *\n     * @param object 请求响应内容\n     */\n    private void checkResponse(JSONObject object) {\n        if (object.containsKey(\"error\")) {\n            throw new AuthException(object.getString(\"error_description\"));\n        }\n    }\n\n    @Override\n    public AuthUser getUserInfo(AuthToken authToken) {\n        String token = authToken.getAccessToken();\n        String tokenType = authToken.getTokenType();\n        String jwt = tokenType + \" \" + token;\n\n        HttpHeader httpHeader = new HttpHeader();\n        httpHeader.add(\"Authorization\", jwt);\n\n        String userInfo = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false).getBody();\n        JSONObject object = JSONObject.parseObject(userInfo);\n        this.checkResponse(object);\n        return AuthUser.builder()\n            .rawUserInfo(object)\n            .uuid(object.getString(\"id\"))","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/justauth/JustAuth/blob/694bbf1b010d93404e3bfb4824d90e9ddfaebebb/src/main/java/me/zhyd/oauth/request/AbstractAuthMicrosoftRequest.java#L60-L96","documentation":"AbstractAuthMicrosoftRequest.checkResponse inspects the Microsoft/Azure AD token response and, if it contains an 'error' key, throws AuthException with the response's error_description. This is Microsoft OAuth passing back a real protocol error: invalid_grant (bad/expired code), invalid_client (wrong client id/secret), or invalid_request (malformed parameters).","triggerScenarios":"Calling getAccessToken with a consumed or expired authorization code (invalid_grant); client secret mismatch or unexpired-credential rotation (invalid_client); redirect_uri differing between authorize and token requests; missing the offline_access/scope needed for a refresh_token when refreshing.","commonSituations":"Azure AD app secrets expire (~1-2 years) and get rotated but the config is not updated; users retry the callback URL so the auth_code is reused; tenant misconfiguration (common vs organizations endpoint) causing AADSTS errors; clock skew causing code expiry.","solutions":["Read the error_description — it contains the AADSTS code; look it up in Microsoft's error reference for the precise cause.","For invalid_client: re-copy clientId/clientSecret from the Azure app registration (Certificates & secrets) and restart/rebuild the request with the new AuthConfig.","For invalid_grant: never reuse an auth code or refresh token; send the user back through authorize(). Ensure redirectUri is byte-identical in both steps and included in the app registration's redirect URIs.","When refreshing, request scope offline_access during authorization or refresh will fail."],"exampleFix":"// before\nAuthConfig cfg = AuthConfig.builder().clientId(id).clientSecret(staleSecret).redirectUri(uri).build(); // invalid_client\n\n// after\nAuthConfig cfg = AuthConfig.builder().clientId(id).clientSecret(currentSecret).redirectUri(uri).build();\n// and ensure the same redirectUri is registered in Azure: App registrations -> Authentication -> Redirect URIs","handlingStrategy":"try-catch","validationCode":"// before token exchange: ensure code present and single-use\nif (StringUtils.isEmpty(callback.getCode())) throw new IllegalArgumentException(\"missing code\");\nif (codeStore.consumeIfAbsent(callback.getCode()) == null) throw new IllegalStateException(\"code already used\");","typeGuard":null,"tryCatchPattern":"try { return request.getAccessToken(cb); } catch (AuthException e) { if (e.getMessage() != null && e.getMessage().contains(\"invalid_grant\")) { return restartAuthorize(); } throw e; }","preventionTips":["Never reuse an auth code — mark it consumed on first exchange.","Keep clientId/clientSecret in a secret manager and rotate before expiry.","Make redirectUri byte-identical between authorize and token calls and register it in Azure.","Request scope offline_access when you need refresh tokens."],"tags":["justauth","microsoft","azure-ad","oauth2","invalid-grant","external-api"],"backgroundTag":null,"analyzedSha":"694bbf1b010d93404e3bfb4824d90e9ddfaebebb","analyzedAt":"2026-08-14T15:16:59.945Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}