# `AshAuthentication.Oauth2Server.Authorize`
[🔗](https://github.com/team-alembic/ash_authentication_oauth2_server/blob/v0.1.0/lib/ash_authentication/oauth2_server/authorize.ex#L5)

Protocol-pure logic for the `/oauth/authorize` endpoint.

Controllers in `ash_authentication_phoenix` are thin wrappers around
`validate_request/2`, `consented?/4`, `grant_consent!/4`, and
`issue_code!/3`. None of these functions touch `Plug.Conn`.

# `validated`

```elixir
@type validated() :: %{
  client: Ash.Resource.record(),
  redirect_uri: String.t(),
  code_challenge: String.t(),
  scope: String.t(),
  state: String.t(),
  resource: String.t()
}
```

The validated authorize-request payload. The struct is intentionally small —
enough to render a consent screen and ultimately mint an authorization code.

# `consented?`

```elixir
@spec consented?(
  server :: module(),
  user :: Ash.Resource.record(),
  client :: Ash.Resource.record(),
  requested_scope :: String.t()
) :: boolean()
```

Has the user already consented to this client at a scope that covers the
currently-requested scope?

Returns true ONLY when prior consent exists AND its scope is a superset of
`requested_scope`. This prevents silent privilege expansion when a client
later asks for more scopes than the user originally agreed to.

# `grant_consent!`

```elixir
@spec grant_consent!(
  server :: module(),
  user :: Ash.Resource.record(),
  client :: Ash.Resource.record(),
  scope :: String.t()
) :: Ash.Resource.record()
```

Record (or refresh) a consent row for `(user, client)` at the given scope.

# `issue_code!`

```elixir
@spec issue_code!(
  server :: module(),
  user :: Ash.Resource.record(),
  validated :: validated()
) :: Ash.Resource.record()
```

Mint a new short-lived authorization code bound to the user, client, scope,
PKCE challenge, and resource URI.

# `validate_request`

```elixir
@spec validate_request(server :: module(), params :: map()) ::
  {:ok, validated()}
  | {:error, :bad_redirect_uri}
  | {:error, String.t(), String.t()}
```

Validate an inbound authorize request.

Returns:

  * `{:ok, validated}` — request is structurally sound and the client +
    redirect_uri are known.
  * `{:error, :bad_redirect_uri}` — redirect_uri is missing or doesn't
    match a registered URI; per RFC 6749 §4.1.2.1 the controller MUST NOT
    redirect.
  * `{:error, error_code, description}` — any other validation error.
    Controllers redirect these errors back to `redirect_uri`.

## A note on the `state` parameter

Clients MUST set `state` to a cryptographically random, unguessable
value (RFC 6749 §10.12 / RFC 9700 §4.7). The server echoes it back via
the redirect so the client can correlate the response with its
pending request — and verify the response didn't come from a CSRF or
injection attack.

This means **clients should NOT use `state` as a stash for
application-level data** like a "return-to" URL or routing hints. That
pattern is unsafe — the value travels through the user-agent and
query string and is reflected back by the server, so any data put in
it can be observed, replayed, or tampered with. Stash that data
server-side (keyed by a fresh `state`), or encode it in a signed
cookie.

We don't enforce a shape or entropy minimum here, but anything other
than a random per-request value defeats the purpose of `state`.

---

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