Packages
attesto_phoenix
0.17.0
2.3.0
2.2.0
2.1.0
2.0.2
2.0.1
2.0.0
1.4.0
1.3.0
1.2.0
1.1.0
1.0.0
0.20.0
0.19.1
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.2
0.14.1
0.14.0
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
Phoenix/Ecto OAuth 2.0 / OIDC authorization server layer over attesto: authorization, token, PAR, revocation, discovery, JWKS, UserInfo, protected-resource plugs, and Ecto-backed token stores.
Current section
Files
Jump to
Current section
Files
lib/attesto_phoenix/schema/pushed_authorization_request.ex
defmodule AttestoPhoenix.Schema.PushedAuthorizationRequest do
@moduledoc """
Ecto schema for a single Pushed Authorization Request (RFC 9126).
A PAR endpoint stores the normalized, validated authorization request
parameters behind a one-time `request_uri` reference
(`urn:ietf:params:oauth:request_uri:…`) and hands that reference to the
client; the client then presents only the `request_uri` at `/authorize`. An
in-memory store cannot share that reference across nodes - a `request_uri`
pushed to one node is unknown to another - so a clustered (or simply
load-balanced) deployment needs the reference in shared storage. This schema
backs `AttestoPhoenix.Store.EctoPARStore`, persisting one row per pushed
request so any node resolves a `request_uri` issued by any other.
## Columns
* `request_uri` - the opaque `urn:ietf:params:oauth:request_uri:` reference
(RFC 9126 §2.2) returned to the client. It is the PRIMARY KEY, so a
reference is stored at most once and the authorization endpoint's lookup
(and the optional single-use `take/1`) hits the primary key directly.
* `params` - the stored, already-validated authorization request parameters
(a string-keyed map; client authentication secrets are dropped before
storage). Persisted as `jsonb`; the authorization endpoint re-runs the
normal `Attesto.AuthorizationRequest` validation after resolving it.
* `expires_at` - the reference's expiry (RFC 9126 §2.2). The store rejects an
expired row on read, so an unswept expired reference is never honored.
* `inserted_at` - when the reference was pushed (diagnostic; never a lookup
key).
"""
use Ecto.Schema
import Ecto.Changeset
@typedoc "A persisted pushed authorization request row."
@type t :: %__MODULE__{
request_uri: String.t() | nil,
params: map() | nil,
expires_at: DateTime.t() | nil,
inserted_at: DateTime.t() | nil
}
# A pushed request is fully described by its reference, the stored params, and
# the two instants; there is no surrogate id and no mutable state, so every
# field is set once at insert.
@insert_fields [:request_uri, :params, :expires_at, :inserted_at]
@primary_key {:request_uri, :string, autogenerate: false}
schema "attesto_pushed_authorization_requests" do
field :params, :map
field :expires_at, :utc_datetime
field :inserted_at, :utc_datetime
end
@doc """
Changeset for storing a freshly pushed authorization request.
Requires the `request_uri` reference, the stored `params`, and both instants.
A reference with no expiry would never fail closed, so a missing `:expires_at`
is a hard validation error rather than a silently unlimited reference. The
`unique_constraint/3` on the primary key surfaces a duplicate `request_uri` as
a changeset error (which `EctoPARStore.put/3` maps to `{:error, _}`) rather
than a raised exception.
"""
@spec put_changeset(t() | %__MODULE__{}, map()) :: Ecto.Changeset.t()
def put_changeset(request \\ %__MODULE__{}, attrs) do
request
|> cast(attrs, @insert_fields)
|> validate_required(@insert_fields)
|> unique_constraint(:request_uri, name: :attesto_pushed_authorization_requests_pkey)
end
end