# invalid_request

The request lacks a parameter, combines parameters wrongly or breaks a rule checked before any grant is evaluated (RFC 6749 §4.1.2.1, §5.2).

## Meaning

Sent by `/authorize`, `/par`, `/token` and `/device_authorization`. In this server:

- `/token`: no `grant_type`; no `code`, `refresh_token` or `device_code` for the grant;
  a code with a PKCE challenge redeemed without `code_verifier`; a malformed Basic
  header, more than one client authentication method, a body `client_id` that differs
  from the Basic one, or a `client_assertion` without its type or of an unsupported type.
- `/authorize`, redirected to the client: a plain request from a client registered to
  require a signed request object, a `response_mode` other than `query` (other than
  `fragment` for an implicit request), no PKCE challenge where the client or organization
  requires one, or a method other than `S256`; a `claims` parameter that is not a JSON
  object of claim requests; and for an implicit request, no `nonce`, no `openid` scope,
  or a `dpop_jkt`. An implicit request's error is in the redirect's fragment.
- `/authorize`, answered directly: an unknown `client_id`, a `redirect_uri` that is not
  registered (or missing while the client has several), a front-channel request where
  PAR is required, a `request_uri` that is unknown, spent or another client's, or a
  request object that does not verify. All of these answer only `invalid_request` and
  `iss`, so an unregistered client and a wrong redirect look the same.
- `/par`: a `request_uri` in the push, a `client_id` other than the authenticated
  client, or any rule `/authorize` applies.

## Impact

Nearly always an integration bug or a registration that does not match the app: the
same request fails the same way every time. A redirected error reaches the app's
callback, and what the user sees there is up to the app. A direct `/authorize` refusal
never returns to the app: the user is left on the server, and a browser on the main
organization sees an error page showing a Reference ID.

## Diagnosis

1. Read `error_description`; it names the rule everywhere except on a direct
   `/authorize` refusal.
2. Paste the response's `X-Request-ID` (or the Reference ID the user saw) into the admin
   dashboard's Troubleshooting page. See
   [Tracing a failed request](https://www.nauthera.io/docs/v1/troubleshooting#tracing-a-failed-request).
3. The audit action is `oauth.authorization.requested` (`/authorize`, `/par`),
   `oauth.token.issued` or `oauth.token.refreshed` (`/token`), `oauth.device.requested`,
   or `oauth.client_authentication.attempted` for a malformed client credential. A direct
   refusal is recorded under the rule as its reason (`unregistered_client`,
   `redirect_uri_required`, `invalid_redirect_uri`, `pushed_request_required`,
   `invalid_request_uri`, `request_uri_client_mismatch`, `invalid_request_object`);
   elsewhere the reason is `invalid_request` and the cause names the rule
   (`parameter_missing`, `pkce_required`, `pkce_method_unsupported`,
   `response_mode_unsupported`, `dpop_jkt_without_code`, `claims_malformed`,
   `signed_request_required`,
   `multiple_auth_methods`, …).
4. Compare the request with the client's registration on the dashboard's client page or
   its `OIDCClient` resource: redirect URIs are matched exactly.

## Mitigation

- As a client developer: fix what the description or the recorded cause names. Send one
  client authentication method, the `redirect_uri` exactly as registered, and an `S256`
  PKCE challenge.
- As an operator: for a direct refusal of a registered app, compare its `redirect_uri`
  with the registration character by character (scheme, port, path, trailing slash).
  With `fapi.require_pushed_authorization_requests` on, every client must use `/par`.
- As a user: go back to the application and start again; if it repeats, give the
  application's support the Reference ID.

## Escalation

If the recorded rule is one the request did not break, or a request accepted before an
upgrade is refused after it, open an issue with the request id, the server version
(`nauthera-server version`), the endpoint and the reason and cause from the audit record.
Never attach codes, tokens, secrets, request objects or client assertions.

---

[OAuth error runbooks](https://www.nauthera.io/docs/v1/runbooks/oauth) · Generated from [`internal/runbook/oauth.yaml`](https://github.com/nauthera/nauthera-server/blob/main/internal/runbook/oauth.yaml) in nauthera-server.
