thephpleague/oauth2-server · error · OAuthServerException

6

6

Error message

The user credentials were incorrect.

What it means

OAuthServerException::invalidCredentials() produces an 'invalid_credentials' error (code 6) with the message 'The user credentials were incorrect.' The password grant throws it when UserRepository::getUserEntityByUserCredentials returns null/false - i.e. no UserEntity matched the supplied username/password for this grant type and client. Before throwing, it emits a USER_AUTHENTICATION_FAILED event so apps can audit or throttle.

Solutions

  1. Verify the username/password pair is correct and the account exists and is active
  2. Confirm your UserRepository returns a UserEntityInterface and hashes are compared with password_verify against the stored hash
  3. Listen for the USER_AUTHENTICATION_FAILED event to log failed attempts and detect lockouts/brute force
  4. Return null rather than throwing from getUserEntityByUserCredentials so the library surfaces the proper OAuth error

Example fix

// before
$user = $this->userRepository->getUserEntityByUserCredentials($username, $password, 'password', $client);
if ($user === null) { return null; }
// after (repository side)
if (password_verify($password, $storedHash)) {
    return $userEntity; // must implement UserEntityInterface
}
return null; // library then throws invalidCredentials with code 6
Defensive patterns

Strategy: try-catch

Validate before calling

if (empty($username) || empty($password)) {
    // fail fast client-side before invoking the grant
    throw new \InvalidArgumentException('Credentials required');
}

Type guard

function credentialsPresent(?string $u, ?string $p): bool {
    return $u !== null && $u !== '' && $p !== null && $p !== '';
}

Try / catch

try {
    $token = $server->respondToAccessTokenRequest($request, $response);
} catch (OAuthServerException $e) {
    if ($e->getMessage() === 'The user credentials were incorrect.') {
        // show generic auth-failure message; consider rate limiting
        return $e->generateHttpResponse($response->withStatus(401));
    }
    throw $e;
}

Prevention

When it happens

Trigger: validateUser in PasswordGrant: getUserEntityByUserCredentials($username, $password, grantType, $client) returns something that is not a UserEntityInterface - wrong password, unknown user, credentials valid only for another grant type, or the repository filtering by client.

Common situations: User typed the wrong password or an email/username variant that isn't registered; password hashing mismatch after migrating users (bcrypt vs argon2); custom user repository returning null for grant_type scoping it doesn't support; locked or soft-deleted user accounts; test fixtures not seeded in a new environment.

Related errors


AI-assisted analysis of thephpleague/oauth2-server@9d2f6fc0a0 (2026-09-15). Data as JSON: /api/errors/08b2e44e2e0e63bd. Report an issue: GitHub.

Appendix: source

Thrown at src/Grant/PasswordGrant.php:101

    protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client): UserEntityInterface
    {
        $username = $this->getRequestParameter('username', $request)
            ?? throw OAuthServerException::invalidRequest('username');

        $password = $this->getRequestParameter('password', $request)
            ?? throw OAuthServerException::invalidRequest('password');

        $user = $this->userRepository->getUserEntityByUserCredentials(
            $username,
            $password,
            $this->getIdentifier(),
            $client
        );

        if ($user instanceof UserEntityInterface === false) {
            $this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));

            throw OAuthServerException::invalidCredentials();
        }

        return $user;
    }

    /**
     * {@inheritdoc}
     */
    public function getIdentifier(): string
    {
        return 'password';
    }
}

View on GitHub (pinned to 9d2f6fc0a0)