# `AttestoPhoenix.Store.EctoCIBAStore`
[🔗](https://github.com/XukuLLC/attesto_phoenix/blob/v2.1.0/lib/attesto_phoenix/store/ecto_ciba_store.ex#L1)

Ecto/Postgres implementation of `Attesto.CIBAStore`.

Every state transition is a single guarded atomic statement, so the CIBA
authentication-request state machine is race-free across nodes:

  * `approve/3` / `deny/2` — `UPDATE ... WHERE status = 'pending' AND
    expires_at > $now RETURNING`, so the user's decision is taken exactly
    once and never lands on an expired request.
  * `poll/2` — one conditional `UPDATE ... SET last_polled_at = $now WHERE
    auth_req_id_hash = $1 AND (last_polled_at IS NULL OR last_polled_at <=
    $now - interval) RETURNING`, enforcing the CIBA Core §7.3 minimum
    token-request interval (the ROW's `interval` column - the value the
    client was told at issue) and reading the row's state in the same
    statement. 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' AND expires_at > $now RETURNING`, so an approved request mints
    exactly one token family.

Backs the schema `AttestoPhoenix.Schema.CIBARequest`. Only the `auth_req_id`'s
hash is stored.

---

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