{"record":{"id":"bba9984c6d5012ad","repo":"justauth/JustAuth","slug":"5003","errorCode":"5003","errorMessage":"Unsupported operation","messagePattern":"Unsupported operation","errorType":"exception","errorClass":"AuthException","httpStatus":null,"severity":"error","filePath":"src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java","lineNumber":102,"sourceCode":"        }\n\n        @Override\n        public Class<? extends AuthDefaultRequest> getTargetClass() {\n            return AuthGiteeRequest.class;\n        }\n    },\n    /**\n     * 钉钉扫码登录\n     */\n    DINGTALK {\n        @Override\n        public String authorize() {\n            return \"https://oapi.dingtalk.com/connect/qrconnect\";\n        }\n\n        @Override\n        public String accessToken() {\n            throw new AuthException(AuthResponseStatus.UNSUPPORTED);\n        }\n\n        @Override\n        public String userInfo() {\n            return \"https://oapi.dingtalk.com/sns/getuserinfo_bycode\";\n        }\n\n        @Override\n        public Class<? extends AuthDefaultRequest> getTargetClass() {\n            return AuthDingTalkRequest.class;\n        }\n    },\n    /**\n     * 新版钉钉扫码登录\n     */\n    DINGTALK_V2 {\n        @Override\n        public String authorize() {","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/justauth/JustAuth/blob/694bbf1b010d93404e3bfb4824d90e9ddfaebebb/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java#L84-L120","documentation":"For the DINGTALK source, the accessToken() endpoint deliberately throws AuthException 5003 (Unsupported operation). DingTalk scan-login (qrconnect) does not use a server-side OAuth access-token exchange: the tmp_auth_code from the callback is sent directly to the getuserinfo_bycode endpoint. Calling any flow step that needs source.accessToken() therefore fails by design.","triggerScenarios":"Calling AuthDingTalkRequest.getAccessToken(...) paths that invoke source.accessToken(), e.g. attempting a token refresh, revoke, or a generic flow that assumes every provider has a token endpoint; calling accessToken() manually on AuthDefaultSource.DINGTALK.","commonSituations":"Treating DingTalk like a standard OAuth2 provider in a generic multi-provider pipeline; calling refresh()/revoke() on a DingTalk request; new code paths added to AbstractAuthDingtalkRequest that assume a token URL exists.","solutions":["Use the DingTalk-specific flow: getAccessToken(callback) just wraps the code, and getUserInfo(token) exchanges the tmp_auth_code via getuserinfo_bycode — call login(callback) or getUserInfo directly.","Do not call refresh() or revoke() on DingTalk requests; guard those calls by provider type.","If you truly need token-based DingTalk APIs, use DingTalk's own SDK instead of this source."],"exampleFix":"// before\nAuthToken token = request.refresh(oldToken).getData(); // -> source.refresh()/accessToken() -> 5003\n\n// after (DingTalk has no token endpoint; use the one-shot code exchange)\nAuthResponse<AuthUser> resp = request.login(AuthCallback.builder().code(code).build());","handlingStrategy":"validation","validationCode":"if (AuthDefaultSource.DINGTALK == source) {\n    // no token endpoint: use login() only; skip refresh/revoke\n    return dingtalkLogin(request, code);\n}","typeGuard":"boolean hasAccessTokenEndpoint(AuthSource s) {\n    try { s.accessToken(); return true; }\n    catch (AuthException e) { return e.getErrcode() == AuthResponseStatus.UNSUPPORTED.getCode() ? false : true; }\n}","tryCatchPattern":"catch (AuthException e) { if (e.getErrcode() == 5003) { /* DingTalk has no token endpoint; use login() flow */ } else throw e; }","preventionTips":["Branch provider flows by capability (token endpoint present?) rather than assuming uniform OAuth2.","Keep a per-provider capability matrix in your auth layer."],"tags":["justauth","dingtalk","unsupported-operation","provider-quirk"],"backgroundTag":null,"analyzedSha":"694bbf1b010d93404e3bfb4824d90e9ddfaebebb","analyzedAt":"2026-08-14T15:16:59.945Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}