{"record":{"id":"d66e76230baa7aab","repo":"langgenius/dify","slug":"client-secret-is-invalid","errorCode":null,"errorMessage":"client_secret is invalid","messagePattern":"client_secret is invalid","errorType":"http","errorClass":"BadRequest","httpStatus":400,"severity":"error","filePath":"api/controllers/console/auth/oauth_server.py","lineNumber":211,"sourceCode":"@console_ns.route(\"/oauth/provider/token\")\nclass OAuthServerUserTokenApi(Resource):\n    @setup_required\n    @console_ns.expect(console_ns.models[OAuthTokenRequest.__name__])\n    @console_ns.response(200, \"Success\", console_ns.models[OAuthProviderTokenResponse.__name__])\n    @oauth_server_client_id_required\n    @model_validate(OAuthTokenRequest)\n    def post(self, payload: OAuthTokenRequest, oauth_provider_app: OAuthProviderApp):\n        try:\n            grant_type = OAuthGrantType(payload.grant_type)\n        except ValueError:\n            raise BadRequest(\"invalid grant_type\")\n        match grant_type:\n            case OAuthGrantType.AUTHORIZATION_CODE:\n                if not payload.code:\n                    raise BadRequest(\"code is required\")\n\n                if payload.client_secret != oauth_provider_app.client_secret:\n                    raise BadRequest(\"client_secret is invalid\")\n\n                if payload.redirect_uri not in oauth_provider_app.redirect_uris:\n                    raise BadRequest(\"redirect_uri is invalid\")\n\n                access_token, refresh_token = OAuthServerService.sign_oauth_access_token(\n                    grant_type, code=payload.code, client_id=oauth_provider_app.client_id\n                )\n                return jsonable_encoder(\n                    {\n                        \"access_token\": access_token,\n                        \"token_type\": \"Bearer\",\n                        \"expires_in\": OAUTH_ACCESS_TOKEN_EXPIRES_IN,\n                        \"refresh_token\": refresh_token,\n                    }\n                )\n            case OAuthGrantType.REFRESH_TOKEN:\n                if not payload.refresh_token:\n                    raise BadRequest(\"refresh_token is required\")","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/auth/oauth_server.py#L193-L229","documentation":"Flask BadRequest (HTTP 400) at oauth_server.py:211 in the AUTHORIZATION_CODE branch of the token endpoint. The code was present and grant_type valid, but payload.client_secret does not equal oauth_provider_app.client_secret. This is a constant-time-unsafe direct string comparison of the confidential client secret.","triggerScenarios":"POST /oauth/provider/token with grant_type=authorization_code, a non-empty code, and a client_secret that does not match the registered OAuthProviderApp. Common after secret rotation or a copy/paste error.","commonSituations":"Client secret was rotated server-side but the client still holds the old value; secret copied with whitespace/newline; or a different app's secret used by mistake.","solutions":["Obtain the current client_secret from the OAuthProviderApp registration and use exactly that value.","After any secret rotation, update all clients immediately and ensure no trailing newline is appended when copying.","Confirm you are pairing the secret with the matching client_id (mismatched pairs fail this check).","Treat the comparison as case- and whitespace-sensitive; strip nothing server-side."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (clientSecret !== registeredSecret) {\n  throw new Error('client_secret mismatch — fetch the current secret');\n}","typeGuard":"function hasClientSecret(s: unknown): s is string { return typeof s === 'string' && s.length > 0; }","tryCatchPattern":"try {\n  await exchangeCodeForToken(code, clientId, clientSecret, redirectUri);\n} catch (e) {\n  if (/client_secret is invalid/i.test(e.message)) { refreshSecret(); } else throw e;\n}","preventionTips":["Store the secret securely and update all clients immediately after rotation.","Pair the secret with its own client_id only.","Avoid copying secrets with trailing newlines."],"tags":["oauth-server","client-secret","token","security"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}