Ithibati.Identity.RecoveryCodes (Ithibati v0.1.0)

Copy Markdown View Source

Single-use codes, for the day a passkey is gone.

A passkey-only account has one credential set, and one lost keychain would otherwise be the end of it. Recovery codes are the second set: twelve codes by default, shown once, each good for exactly one sign-in. The application sets how many, and whether spending the last one brings a fresh batch.

Summary

Functions

Redeems a code: marks it spent and answers the account that held it.

Issues a fresh batch, and invalidates every code the account already had, spent or not.

How many unused codes this account has left.

Functions

redeem(code, opts \\ [])

Redeems a code: marks it spent and answers the account that held it.

It answers {:ok, account, codes}, where codes is a fresh batch when this was the account's last unused code and nil otherwise. The result has three elements rather than an optional key, so a caller cannot match the common case and silently drop the batch in the one case it exists for.

That refill happens in the same transaction, and refill: false turns it off. It is on by default because of the shape of the failure. An account with no passkey and no codes left is locked out of a self-hosted instance for good, and the only moment anybody can write down a new batch is the one where they have just used the last old one.

{:error, :invalid} covers a code nobody holds and one already spent. Nothing tells the two apart, deliberately.

regenerate(account, opts \\ [])

Issues a fresh batch, and invalidates every code the account already had, spent or not.

An account gets its first batch the same way, with nothing to invalidate yet. What comes back is the plaintext, once. The rows hold digests, and nothing can recover the plaintext afterwards.

:count says how many, and defaults to twelve.

remaining(account)

How many unused codes this account has left.