AttestoPhoenix.Controller.AuthorizeController (AttestoPhoenix v0.6.1)

Copy Markdown View Source

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.

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), 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).

Summary

Functions

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

Functions

authorize(conn, params)

@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.