v0.14 is out: a mobile auth pack for Swift/iOS and Android, catching insecure token storage, cleartext traffic, and OAuth in embedded WebViews. Read more →
HIGH AI PREVALENCE: MEDIUM auth.java.oauth.ropc-grant

OAuth token request uses the Resource Owner Password Credentials grant (grant_type=password).

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.

// oauthlint-disable-next-line auth.java.oauth.ropc-grant -- <reason>

References

https://datatracker.ietf.org/doc/html/rfc9700#section-2.4 ↗https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1#section-2.4 ↗https://cwe.mitre.org/data/definitions/522.html ↗