Ecto schema + record bridge for the RFC 8628 device-code store
(AttestoPhoenix.Store.EctoDeviceCodeStore).
Backs Attesto.DeviceCodeStore: a device code is a mutable row that moves
through pending → (approved | denied) → consumed. from_record/1
spreads the core's Attesto.DeviceCodeStore.entry() map across the row's
columns for the initial pending insert; to_entry/1 folds a loaded row back
into that contract shape. The mutating transitions are done as guarded atomic
UPDATEs in the store, not through this changeset.
Only the device code's Attesto.Secret.hash/1 is stored, never the plaintext;
user_code is stored normalized.
Summary
Functions
Build the insert changeset for a new pending device code from the core
store record. The issue-time :data map must contain exactly the four atom
keys emitted by Attesto.DeviceCode.issue/3; missing or extra keys raise a
fixed, value-free ArgumentError before any field is projected into the row.
The write envelope must also be pending with a valid expiry, and undecided:
status must be explicit, while decision fields and last_polled_at must be
nil or absent. Other missing required record fields remain changeset
validation errors.
Fold a loaded row into the Attesto.DeviceCodeStore.entry() contract shape.
The non-consuming Attesto.DeviceCodeStore.pending_view() for the verification
page.
Types
@type t() :: %AttestoPhoenix.Schema.DeviceCode{ __meta__: term(), client_id: term(), device_code_hash: term(), dpop_jkt: term(), expires_at: term(), granted_claims: term(), granted_scope: term(), id: term(), inserted_at: term(), last_polled_at: term(), resource: term(), scope: term(), status: term(), subject: term(), user_code: term() }
Functions
@spec from_record(Attesto.DeviceCodeStore.entry(), keyword()) :: Ecto.Changeset.t()
Build the insert changeset for a new pending device code from the core
store record. The issue-time :data map must contain exactly the four atom
keys emitted by Attesto.DeviceCode.issue/3; missing or extra keys raise a
fixed, value-free ArgumentError before any field is projected into the row.
The write envelope must also be pending with a valid expiry, and undecided:
status must be explicit, while decision fields and last_polled_at must be
nil or absent. Other missing required record fields remain changeset
validation errors.
@spec to_entry(t()) :: Attesto.DeviceCodeStore.entry()
Fold a loaded row into the Attesto.DeviceCodeStore.entry() contract shape.
@spec to_pending_view(t()) :: Attesto.DeviceCodeStore.pending_view()
The non-consuming Attesto.DeviceCodeStore.pending_view() for the verification
page.