thephpleague/oauth2-server · error · OAuthServerException

invalid_request

invalid_request

Error message

The request is missing a required parameter, is invalid, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the "username" parameter

What it means

PasswordGrant::validateUser throws invalidRequest('username') when the token request body has no 'username' parameter. The password grant mandates both username and password in the POST body (application/x-www-form-urlencoded). The generic invalid_request message names the offending parameter to help the caller fix the request.

Solutions

  1. Send the token request as form-encoded with a username field: POST grant_type=password&username=...&password=...
  2. If your client sends JSON, switch to form data or configure the server to parse JSON bodies before calling the grant
  3. Check for typos in parameter names on the client side
  4. Log the parsed body server-side to confirm what the grant actually receives

Example fix

// before
await fetch('/token', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({grant_type:'password', username, password}) });
// after
await fetch('/token', { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({grant_type:'password', username, password}) });
Defensive patterns

Strategy: validation

Validate before calling

$params = (array) $request->getParsedBody();
if (!isset($params['username']) || !is_string($params['username']) || $params['username'] === '') {
    throw new \InvalidArgumentException('username required');
}

Type guard

function hasUsername(array $params): bool {
    return isset($params['username']) && is_string($params['username']) && $params['username'] !== '';
}

Try / catch

try {
    $token = $server->respondToAccessTokenRequest($request, $response);
} catch (OAuthServerException $e) {
    if (str_contains($e->getMessage(), '"username"')) {
        return $e->generateHttpResponse($response->withStatus(400));
    }
    throw $e;
}

Prevention

When it happens

Trigger: POST to the token endpoint with grant_type=password but no username field; getRequestParameter('username', $request) returns null so the ?? throw triggers; validateUser is invoked from respondToAccessTokenRequest.

Common situations: Client sends JSON body while the server only parses form-encoded parameters; parameter name typo (user_name, email); frontend forgets to include username in the token request; middleware strips or rewrites the request body.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at src/Grant/PasswordGrant.php:86

        // Issue and persist new refresh token if given
        $refreshToken = $this->issueRefreshToken($accessToken);

        if ($refreshToken !== null) {
            $this->getEmitter()->emit(new RequestRefreshTokenEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request, $refreshToken));
            $responseType->setRefreshToken($refreshToken);
        }

        return $responseType;
    }

    /**
     * @throws OAuthServerException
     */
    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;

View on GitHub (pinned to 9d2f6fc0a0)