Latch. Store behaviour
(latch v0.5.0)
Copy Markdown
Storage backend for in-flight OAuth requests and long-lived sessions.
Implement this behavior in your app. The library is storage-agnostic. It hands you plains tructs and expects you to persist them however you like (Ecto, Redis, ETS, ...). The library never sees encryption or the database, that's your concerns.
Requests are keyed by the OAuth state.
Sessions are keyed by the user's DID.
Summary
Callbacks
Delete the session for did. Return :ok even if missing.
Fetch the session for did.
Store an inflight request under state for at least ttl_seconds.
Insert or replace session for did.
Atomically fetch and remove the request for state.
Run fun against the session for did under an exclusive lock, within
an atomic transaction. Rotating refresh tokens are single use, so two
concurrent refreshes must run in serial.
Types
Callbacks
@callback delete_session(did()) :: :ok | store_error()
Delete the session for did. Return :ok even if missing.
@callback fetch_session(did()) :: {:ok, Latch.Session.t()} | store_error()
Fetch the session for did.
@callback put_request(state(), Latch.Request.t(), ttl_seconds :: pos_integer()) :: :ok | store_error()
Store an inflight request under state for at least ttl_seconds.
@callback put_session(did(), Latch.Session.t()) :: :ok | store_error()
Insert or replace session for did.
@callback take_request(state()) :: {:ok, Latch.Request.t()} | store_error()
Atomically fetch and remove the request for state.
Single use. Must be consumable exactly once, even under concurrency.
Return {:error, :not_found} if used or non-existant.
@callback update_session(did(), (Latch.Session.t() -> {:ok, Latch.Session.t()} | {:error, term()})) :: {:ok, Latch.Session.t()} | store_error()
Run fun against the session for did under an exclusive lock, within
an atomic transaction. Rotating refresh tokens are single use, so two
concurrent refreshes must run in serial.
Use FOR UPDATE or CAS to make atomic.
{:ok, new_session}persistsnew_sessioninside the same transaction and returns it.{:error, reason}rolled back, reason propagated.
{:error, :not_found} if no session exists for did.