Attesto.CodeStore.ETS (Attesto v2.6.1)

Copy Markdown View Source

Single-node ETS implementation of Attesto.CodeStore.

Codes live in an ETS table owned by a GenServer that sweeps expired rows on a fixed interval. take/1 uses :ets.take/2, which fetches and deletes a row in one atomic step, so the single-use guarantee holds against concurrent redemptions on a node. This is a per-node store: a multi-node deployment MUST back Attesto.CodeStore with a shared store (e.g. Postgres DELETE ... RETURNING) so a code issued on one node can be redeemed (once) on another.

Successful redemptions support the optional mark_consumed/2 reuse contract. Markers for bounded refresh families live through their family deadline; unbounded refresh-family markers remain sticky for the store's lifetime. A no-refresh marker is retained for 24 hours, bounding memory while covering the normal access-token replay-response window.

The store also indexes the JTI of an access token minted from a redeemed code. On a later code replay, callers can revoke that token by code hash or by the finalized refresh-family ID and query the revocation by JTI. Both the linkage and revocation marker expire with the access token.

Start options

  • :sweep_interval_ms (default 30_000) - how often expired rows are bulk-deleted. Correctness does not depend on sweeping (take/1 returns the row and Attesto.AuthorizationCode re-checks expiry); the sweeper only bounds table size.

Wiring

children = [Attesto.CodeStore.ETS]

then pass the module as the store:

Attesto.AuthorizationCode.issue(Attesto.CodeStore.ETS, attrs)

Summary

Functions

Return whether a linked, unexpired access-token JTI has been revoked.

Link a minted access-token JTI to its redeemed code until token expiry.

Clear every entry. Test-facing.

Revoke the unexpired access token linked to a code hash.

Revoke unexpired access tokens linked to a finalized refresh family.

Functions

access_token_revoked?(jti)

@spec access_token_revoked?(String.t()) :: boolean()

Return whether a linked, unexpired access-token JTI has been revoked.

record_access_token_for_code(code_hash, jti, expires_at)

@spec record_access_token_for_code(String.t(), String.t(), integer()) :: :ok

Link a minted access-token JTI to its redeemed code until token expiry.

reset()

@spec reset() :: :ok

Clear every entry. Test-facing.

revoke_access_token_for_code(code_hash)

@spec revoke_access_token_for_code(String.t()) :: :ok

Revoke the unexpired access token linked to a code hash.

revoke_family_access_tokens(family_id)

@spec revoke_family_access_tokens(String.t()) :: :ok

Revoke unexpired access tokens linked to a finalized refresh family.

start_link(opts \\ [])

@spec start_link(keyword()) :: GenServer.on_start()