# `AttestoPhoenix.AuthorizationServer.RequestPolicy`
[🔗](https://github.com/XukuLLC/attesto_phoenix/blob/v0.19.0/lib/attesto_phoenix/authorization_server/request_policy.ex#L1)

Conn-free resolution of the per-request authorization-request validation
policy shared by the authorization endpoint and the PAR endpoint.

Both endpoints validate the same authorization request the same way (RFC 9126
§2.1: "validate the pushed request as it would an authorization request sent
to the authorization endpoint"), so the policy inputs `Attesto.AuthorizationRequest.validate/2`
needs - the client's registered redirect URIs (RFC 6749 §3.1.2.3), whether
PKCE is required (RFC 9700 §2.1.1), and whether `nonce` is required (OIDC Core
§3.1.2.1) - are resolved here once, from `%AttestoPhoenix.Config{}` and the
opaque host client, rather than duplicated per endpoint. This module reads
only data: it touches no `conn` and carries no policy of its own beyond the
fail-closed defaults documented on each function.

# `client_public?`

```elixir
@spec client_public?(AttestoPhoenix.Config.t(), term()) :: boolean()
```

Classify the client as public via the host's `:client_public?` callback.

Absent the callback, fail closed by treating the client as public, so PKCE
stays required (a confidential exemption demands a deliberate host
classification).

# `registered_redirect_uris`

```elixir
@spec registered_redirect_uris(AttestoPhoenix.Config.t(), term()) :: [String.t()]
```

The client's registered redirect URIs (RFC 6749 §3.1.2.3).

For a CIMD client (`{:cimd, metadata}`,
`draft-ietf-oauth-client-id-metadata-document-01`) the document *is* the
registration, so the registered set is the document's own `redirect_uris`
(RFC 9700) and the host's per-client callback is never consulted. For a
registered client the set is resolved through the host's
`:client_redirect_uris` callback; an absent callback or a non-list return
resolves to `[]`, which rejects every request with an unregistered redirect
URI (fail closed).

# `require_nonce?`

```elixir
@spec require_nonce?(AttestoPhoenix.Config.t()) :: boolean()
```

The host's OP nonce policy flag (OIDC Core §3.1.2.1).

Returns the raw `:require_nonce` configuration. The OIDC openid-scope gate is
NOT applied here: it must run on the EFFECTIVE request (after any signed
`request` object is merged), which only `Attesto.AuthorizationRequest.validate/2`
sees. Applying the gate on the raw outer params here would let a direct JAR
carrying `scope=openid` only inside the signed object bypass the requirement.

# `require_pkce?`

```elixir
@spec require_pkce?(AttestoPhoenix.Config.t(), term()) :: boolean()
```

Whether PKCE is required for this client (RFC 7636 §4.3 / RFC 9700 §2.1.1).

A public client MUST use PKCE, so `client_public?/2` forces it regardless of
config. A sender-constrained client (DPoP or mTLS) is a FAPI 2.0 client, and
FAPI 2.0 Security Profile §5.3.1.2 / RFC 9700 §2.1.1 require PKCE for it even
though it authenticates confidentially - so `client_requires_dpop?/2` and
`client_requires_mtls?/2` force it too. For any other confidential client the
global `:require_pkce` flag applies (default `true`). Fail closed: absent the
host's deliberate opt-out, PKCE is required.

# `validate`

```elixir
@spec validate(AttestoPhoenix.Config.t(), term(), map(), keyword()) ::
  {:ok, Attesto.AuthorizationRequest.t()}
  | {:error, Attesto.AuthorizationRequest.error()}
```

Validate `params` as an authorization request for `client`, resolving the
redirect-URI/PKCE/nonce policy from `config` and delegating to
`Attesto.AuthorizationRequest.validate/2`.

This is the shared entry point both the authorization endpoint and the PAR
endpoint use so a request is validated identically wherever it arrives
(RFC 9126 §2.1). Pass `extra_opts` to thread request-object verification
inputs (`:request_object_jwks`, `:request_object_audience`,
`:request_object_policy`) when the `params` still carry an unverified signed
`request` object; omit them when the object has already been verified and
merged.

---

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