AttestoPhoenix.Schema.ConsentGrant (AttestoPhoenix v0.19.0)

Copy Markdown View Source

Ecto schema for a single-use, request-bound consent grant (RFC 6749 §4.1.1).

Backs AttestoPhoenix.Store.EctoConsentGrantStore. The host consent screen mints one row per Authorize click, keyed on an unguessable token and carrying a binding_hash over the exact request the user saw (subject + client_id + redirect_uri + the requested scope set + code_challenge + code_challenge_method, built by AttestoPhoenix.ConsentGrant). The authorization-server :consent callback recomputes the hash from the live request, looks the row up by token, verifies the hash matches, and consumes it (single use) before a code is issued.

Columns

  • token - the opaque, unguessable grant token (the PRIMARY KEY), so the consume's conditional UPDATE and the disambiguation read both hit the primary key directly. Never the plaintext of any other credential; the token is the grant's only secret.
  • binding_hash - the canonical SHA-256 hash over the bound request fields (AttestoPhoenix.ConsentGrant.binding_hash/1). The consume matches on it, so a grant only ever approves the request it was minted for.
  • subject - the OIDC sub of the resource owner who consented (diagnostic; the subject is also folded into binding_hash).
  • consumed_at - NULL until the single-use claim stamps it; a non-NULL value means the grant was already spent.
  • expires_at - the grant's short TTL (RFC 6749 §4.1.2: codes — and the consent that precedes them — are short-lived). The consume rejects an expired row, so an unswept expired grant is never honored.

Summary

Types

t()

A persisted consent-grant row.

Functions

Changeset for storing a freshly minted consent grant.

Types

t()

@type t() :: %AttestoPhoenix.Schema.ConsentGrant{
  __meta__: term(),
  binding_hash: String.t() | nil,
  consumed_at: DateTime.t() | nil,
  expires_at: DateTime.t() | nil,
  inserted_at: DateTime.t() | nil,
  subject: String.t() | nil,
  token: String.t() | nil,
  updated_at: DateTime.t() | nil
}

A persisted consent-grant row.

Functions

changeset(grant \\ %__MODULE__{}, attrs)

@spec changeset(
  t()
  | %AttestoPhoenix.Schema.ConsentGrant{
      __meta__: term(),
      binding_hash: term(),
      consumed_at: term(),
      expires_at: term(),
      inserted_at: term(),
      subject: term(),
      token: term(),
      updated_at: term()
    },
  map()
) :: Ecto.Changeset.t()

Changeset for storing a freshly minted consent grant.

Requires the token, the binding_hash, the subject, and the expires_at. A grant with no expiry would never fail closed, so a missing :expires_at is a hard validation error rather than a silently unlimited grant. The unique_constraint/3 on the primary key surfaces a duplicate token as a changeset error rather than a raised exception.