# `AttestoPhoenix.Controller.AuthorizeController`
[🔗](https://github.com/XukuLLC/attesto_phoenix/blob/v0.19.0/lib/attesto_phoenix/controller/authorize_controller.ex#L1)

OAuth 2.0 / OpenID Connect authorization endpoint (RFC 6749 §3.1,
OIDC Core §3.1.2).

Handles `GET /oauth/authorize`, the front-channel browser flow that ends in
an authorization code (RFC 6749 §4.1). This module owns only the HTTP and
protocol-framing concerns: it parses and validates the request through
`Attesto.AuthorizationRequest`, classifies failures by where they may be
reported (OIDC Core §3.1.2.6), and on success mints a single-use code through
`Attesto.AuthorizationCode.issue/3` and redirects back to the client. Every
identity decision - who the resource owner is, and whether they consent - is
delegated to host callbacks on `AttestoPhoenix.Config`. No login or consent
UI lives here.

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

The `client_id` and `redirect_uri` are validated BEFORE anything is rendered
or redirected. A request whose `client_id` is unknown, or whose
`redirect_uri` does not exactly match the client's registered set, is
untrusted: the server MUST NOT redirect back to the supplied URI (that would
be an open redirect), so it renders a direct error page to the user agent.
Only once the `client_id`/`redirect_uri` pair is established as trusted is any
further error (bad `response_type`, `scope`, PKCE, `max_age`) reported by
redirecting back to the validated `redirect_uri` with an `error` (and
`error_description`/`state`) query parameter. `Attesto.AuthorizationRequest`
performs this classification; this controller turns each class into the
correct HTTP response.

## PKCE is mandatory (RFC 7636)

`Attesto.AuthorizationRequest` requires a valid S256 `code_challenge`; there
is no PKCE-less path. The challenge is carried into the issued code so the
token endpoint can verify the matching verifier on redemption.

## Resource-owner authentication and consent (host policy)

Authenticating the end user and obtaining consent are host policy, not
protocol, so they are delegated to two `AttestoPhoenix.Config` callbacks:

  * `:authenticate_resource_owner` - `(conn, request, auth_opts ->
    {:authenticated, subject} | {:halt, conn} | {:none} | {:error,
    :login_required | :consent_required | :interaction_required})`. Returns
    `{:authenticated, subject}` once a resource owner is established for this
    request, `{:halt, conn}` to take over the connection (e.g. redirect to a
    login page that, after login, re-enters this endpoint with the same
    authorization parameters), `{:none}` when no subject can be established
    without UI, or an `{:error, _}` to explicitly classify why interaction is
    required (OIDC Core §3.1.2.6). The `subject` is a map carrying at least
    `:subject` (the subject identifier, OIDC Core §2 `sub`) and optionally
    `:auth_time`, `:acr`, and `:amr` (OIDC Core §2), plus `:sid` (the host's
    session id, OIDC Back-Channel Logout 1.0 §2.1 — supply it to make the
    session reachable by the end-session endpoint), threaded into the code's
    claims so the token endpoint can mint the ID token.

    `auth_opts` is a map carrying the OIDC Core §3.1.2.1 authentication
    directives the host MUST honour: `:prompt` (the parsed `prompt` list),
    `:force_reauth` (`true` for `prompt=login`: reauthenticate even if a
    session exists, returning a fresh `auth_time`), `:interactive` (`false`
    for `prompt=none`: the host MUST NOT render any UI, returning
    `{:authenticated, subject}` only if it can be established silently, else
    `{:none}`), and `:max_age` (when present, the host MUST reauthenticate if
    the existing authentication is older and return the resulting
    `auth_time`). Under `prompt=none` the controller converts a `{:halt,
    conn}` or `{:none}` into a `login_required` redirect rather than letting
    any UI run; a `{:halt, conn}` consent screen becomes `consent_required`
    (OIDC Core §3.1.2.6).

  * `:consent` - `(conn, request, subject -> {:consented, subject} |
    {:halt, conn} | {:denied, reason})`. Returns `{:consented, subject}` once
    the resource owner has authorized the request (the returned `subject` may
    carry consent-derived claims), `{:halt, conn}` to take over the
    connection (e.g. render a consent screen that re-enters this endpoint), or
    `{:denied, _reason}` to refuse, which is reported back to the client as
    the RFC 6749 §4.1.2.1 `access_denied` error by redirect. When the host
    does not supply `:consent`, consent is treated as implicitly granted for
    the authenticated subject.

Both callbacks may hand control back to a host-rendered page; the controller
only proceeds to mint a code when both yield a subject. The actual login and
consent UI lives in the host application, never in this library.

## Configuration contract

All host policy is resolved through `AttestoPhoenix.Config`; nothing is
hardcoded here. This controller reads (see `AttestoPhoenix.Config` for the
authoritative definitions and defaults):

  * `:load_client` - client lookup and revocation gate (RFC 6749 §2.2). An
    unknown or revoked client is a direct (non-redirectable) error.
  * `:client_redirect_uris` - `(client -> [String.t()])` the client's
    registered redirect URIs, the trusted set the request `redirect_uri` is
    exact-matched against (RFC 6749 §3.1.2.3).
  * `:client_id` - `(client -> String.t())` the client's identifier, carried
    into the issued code.
  * `:authenticate_resource_owner`, `:consent` - the host login/consent hooks
    described above.
  * `:code_store` - the `Attesto.CodeStore` backing the issued code.
  * `:authorization_code_ttl` - the code lifetime, seconds.
  * `:on_event` - the optional audit/telemetry hook (via
    `AttestoPhoenix.Event`).

# `authorize`

```elixir
@spec authorize(Plug.Conn.t(), map()) :: Plug.Conn.t()
```

Authorization endpoint action (RFC 6749 §3.1, OIDC Core §3.1.2).

Validates the request, authenticates and obtains consent from the resource
owner via host callbacks, issues a single-use authorization code, and
302-redirects back to the client's `redirect_uri` with `code` (and `state`,
when present). Failures are dispatched to a direct error page or a redirected
error per the classification in the moduledoc.

---

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