Why AI tools produce this: AI coding tools produce this regularly, typically when prompted for a shortcut or a quick fix.
Why this matters
The application collects the user's password and replays it to the authorization server, exactly what OAuth was designed to avoid. It cannot support federation, MFA, or step-up auth, and any compromise of your service exposes raw user passwords (CWE-522).
The OAuth 2.0 Security BCP (RFC 9700 §2.4) forbids ROPC and OAuth 2.1 removes it entirely. Use the authorization-code flow with PKCE (grant_type=authorization_code) for user login, or client_credentials for machine-to-machine.
VULNERABLE
vulnerable.java
import okhttp3.FormBody;import okhttp3.RequestBody;import org.springframework.util.LinkedMultiValueMap;import org.springframework.util.MultiValueMap;class TokenClient { // Spring WebClient / RestTemplate form body built as a MultiValueMap. MultiValueMap<String, String> springBody(String username, String password) { MultiValueMap<String, String> form = new LinkedMultiValueMap<>(); // ruleid: auth.java.oauth.ropc-grant form.add("grant_type", "password"); form.add("username", username); form.add("password", password); return form; } // OkHttp form body builder. RequestBody okhttpBody(String username, String password) { // ruleid: auth.java.oauth.ropc-grant return new FormBody.Builder() .add("grant_type", "password") .add("username", username) .add("password", password) .build(); } // Raw URL-encoded request body string. String rawBody(String username, String password) { // ruleid: auth.java.oauth.ropc-grant return "grant_type=password&username=" + username + "&password=" + password; }}
SAFE
safe.java
import okhttp3.FormBody;import okhttp3.RequestBody;import org.springframework.util.LinkedMultiValueMap;import org.springframework.util.MultiValueMap;class TokenClient { // Authorization-code exchange — the correct user-login grant. MultiValueMap<String, String> authCodeBody(String code, String verifier) { // ok: auth.java.oauth.ropc-grant MultiValueMap<String, String> form = new LinkedMultiValueMap<>(); form.add("grant_type", "authorization_code"); form.add("code", code); form.add("code_verifier", verifier); return form; } // Client-credentials grant for machine-to-machine. RequestBody clientCredsBody() { // ok: auth.java.oauth.ropc-grant return new FormBody.Builder() .add("grant_type", "client_credentials") .add("scope", "read") .build(); } // True-negative trap: an unrelated field that merely contains the substring // "password", and a password-reset action — neither is a ROPC token request. String resetBody(String email) { // ok: auth.java.oauth.ropc-grant return "action=password_reset&email=" + email; } String labelledField(String value) { // ok: auth.java.oauth.ropc-grant return "new_password=" + value; }}
Suppressing this rule
If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.