# `AttestoPhoenix.Store.EctoDeviceCodeStore`
[🔗](https://github.com/XukuLLC/attesto_phoenix/blob/v0.19.0/lib/attesto_phoenix/store/ecto_device_code_store.ex#L1)

Ecto/Postgres implementation of `Attesto.DeviceCodeStore`.

Every state transition is a single guarded atomic statement, so the RFC 8628
device-code state machine is race-free across nodes:

  * `approve/2` / `deny/2` — `UPDATE ... WHERE status = 'pending' RETURNING`, so
    the user's decision is taken exactly once even under concurrent posts.
  * `poll/2` — one conditional `UPDATE ... SET last_polled_at = now WHERE
    device_code_hash = $1 AND (last_polled_at IS NULL OR last_polled_at <=
    now - interval) RETURNING`, enforcing the §3.5 minimum poll interval and
    reading the row's state in the same statement (no read-then-write race
    against a concurrent approval). A zero-row result is disambiguated as
    `slow_down` vs unknown by one follow-up existence check (both are non-mint
    outcomes, so it is not a security race).
  * `consume/2` — `UPDATE ... SET status = 'consumed' WHERE status = 'approved'
    RETURNING`, so an approved code mints exactly one token family.

Backs the schema `AttestoPhoenix.Schema.DeviceCode`. Only the device code's
hash is stored; `user_code` is stored normalized.

---

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