# `Attesto.AuthorizationRequest`
[🔗](https://github.com/XukuLLC/attesto/blob/v0.13.0/lib/attesto/authorization_request.ex#L1)

Authorization endpoint request validation (RFC 6749 §4.1.1, OIDC Core
§3.1.2.1, RFC 7636 §4.3).

This module validates the *protocol* shape of an authorization request that
the transport layer has already parsed out of the query string. It checks the
`response_type`, the presence of `client_id` and `redirect_uri`, the requested
scope (surfacing whether the OpenID Connect `openid` scope was requested), and
the PKCE parameters (`code_challenge` + `code_challenge_method`). It carries
`state`, `nonce`, `claims`, and the optional `prompt` / `max_age` / `acr_values`
parameters through to the normalized result.

It deliberately does NOT:

  * authenticate the resource owner or render consent (host policy, performed
    in the Phoenix layer);
  * decide whether the `client_id` exists, beyond requiring it to be present;
  * issue an authorization code (`Attesto.AuthorizationCode.issue/3` does that,
    consuming the normalized request this module returns).

It DOES check `redirect_uri` against the registered set the caller passes in
`:registered_redirect_uris` by exact string match (RFC 6749 §3.1.2.3, OIDC
Core §3.1.2.1): the registered set is a fact the host supplies, not a policy
decision this module makes.

## Error disposition (OIDC Core §3.1.2.6, RFC 6749 §4.1.2.1)

RFC 6749 §4.1.2.1 and OIDC Core §3.1.2.6 split authorization errors into two
classes by *where the error may be reported*:

  * `{:error, {:direct, reason}}` - the request `client_id` or `redirect_uri`
    is missing or invalid. The authorization server MUST NOT redirect back to
    the supplied URI (it is untrusted); the error is shown directly to the
    user agent. Reasons: `:invalid_client_id`, `:missing_redirect_uri`,
    `:invalid_redirect_uri`, `:redirect_uri_not_registered`.

  * `{:error, {:redirect, error}}` - the `client_id`/`redirect_uri` pair is
    trusted but some other parameter is invalid. The server redirects back to
    the validated `redirect_uri` with an `error` (and `error_description`)
    query parameter, echoing `state` when present. The `error` map carries:
    `:error` (the RFC 6749 §4.1.2.1 code), `:error_description`,
    `:redirect_uri`, and `:state`.

The Phoenix layer turns each class into the correct HTTP response; this core
module only classifies.

# `error`

```elixir
@type error() ::
  {:direct,
   :invalid_client_id
   | :missing_redirect_uri
   | :invalid_redirect_uri
   | :redirect_uri_not_registered}
  | {:redirect, redirect_error()}
```

The classification of a validation failure (OIDC Core §3.1.2.6).

# `redirect_error`

```elixir
@type redirect_error() :: %{
  :error =&gt; String.t(),
  :error_description =&gt; String.t(),
  :redirect_uri =&gt; String.t(),
  :state =&gt; String.t() | nil,
  optional(:response_mode) =&gt; String.t() | nil,
  optional(:client_id) =&gt; String.t()
}
```

A redirectable authorization error (RFC 6749 §4.1.2.1).

Errors raised once the client is trusted (from `validate_redirectable/5`) also
carry `:response_mode` (the requested JARM mode, or `nil`) and `:client_id`,
so the transport can return the error in the requested response mode
(JARM §2.3); earlier errors omit them.

# `t`

```elixir
@type t() :: %Attesto.AuthorizationRequest{
  acr_values: [String.t()],
  claims: map(),
  client_id: String.t(),
  code_challenge: String.t() | nil,
  code_challenge_method: String.t() | nil,
  dpop_jkt: String.t() | nil,
  max_age: non_neg_integer() | nil,
  nonce: String.t() | nil,
  openid?: boolean(),
  prompt: [String.t()],
  redirect_uri: String.t(),
  resource: [String.t()],
  response_mode: String.t() | nil,
  response_type: String.t(),
  scope: [String.t()],
  state: String.t() | nil
}
```

A normalized, validated authorization request.

The binding fields (`client_id`, `redirect_uri`, `scope`, `code_challenge`,
`code_challenge_method`, `nonce`) line up with the attrs
`Attesto.AuthorizationCode.issue/3` consumes.

# `supported_response_modes`

```elixir
@spec supported_response_modes() :: [String.t()]
```

The response modes this authorization-code server accepts (OAuth 2.0 Response
Modes / JARM §2.3): `query` and the JARM JWT modes. Exposed so the discovery
document advertises exactly what `validate/2` enforces.

# `validate`

```elixir
@spec validate(
  map(),
  keyword()
) :: {:ok, t()} | {:error, error()}
```

Validate a parsed authorization request parameter map (RFC 6749 §4.1.1,
OIDC Core §3.1.2.1, RFC 7636 §4.3).

`params` is a string-keyed map of the authorization request query parameters.

## Options

  * `:registered_redirect_uris` (required) - the list of redirect URIs
    registered for the client. The request `redirect_uri` MUST be an exact
    string match against one of these (RFC 6749 §3.1.2.3, OIDC Core
    §3.1.2.1). An empty list rejects every request with
    `{:direct, :redirect_uri_not_registered}`.

  * `:require_nonce` (optional, default `false`) - the host's OP nonce policy.
    When `true`, an OpenID Connect Authentication Request (one whose EFFECTIVE
    scope - after any signed `request` object is merged - carries `openid`)
    with no `nonce` is rejected with a redirectable `invalid_request` error
    (OIDC Core §3.1.2.1). The openid test runs on the merged request, so a
    `scope=openid` carried only inside a signed request object still triggers
    the requirement. A plain OAuth request (no `openid` scope) is never
    nonce-constrained (RFC 6749 keeps `code` at SHOULD). When `false`, `nonce`
    stays OPTIONAL and is carried through unenforced.

  * `:require_pkce` (optional, default `true`) - when `true`, a request with no
    `code_challenge` is rejected with a redirectable `invalid_request` error
    (RFC 7636 §4.3). When `false`, an absent `code_challenge` is permitted and
    the validated request carries none. The caller MUST pass `false` only for a
    confidential client: RFC 9700 §2.1.1 keeps PKCE a MUST for public clients.
    A `code_challenge` that IS present is fully enforced (S256, no `plain`)
    regardless of this flag - presence means the client opted into PKCE, so a
    downgrade is always rejected.

  * `:request_object_policy` (optional, default `%Attesto.RequestObject.Policy{}`)
    - the JAR verification policy for a signed `request` object (RFC 9101),
    threaded into `Attesto.RequestObject.verify/3`. The default is the generic
    OpenID Connect §6.1 baseline (`nbf`/`exp`/`typ` not required). For FAPI 2.0
    Message Signing §5.3.1 pass `Attesto.RequestObject.Policy.fapi_message_signing/0`
    and set `:request_object_audience` to the AS issuer. Has no effect unless a
    `request` object is present.

Returns `{:ok, %Attesto.AuthorizationRequest{}}` or `{:error, error()}`, where
`error()` is classified per the moduledoc.

The `client_id` / `redirect_uri` checks run first, because their failure is
non-redirectable (OIDC Core §3.1.2.6): only once a trusted `redirect_uri` is
established may any further error be reported by redirecting to it.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
