Atomically claims a TOTP step for a principal.
Succeeds only when step is greater than the last claimed step, so the
same code — and any earlier code — cannot be replayed. Returns true
when the step was claimed, false when it was at or below the last
claimed step (replay) or no enrolment exists.
Clears the attempt count for a principal (called on successful verification).
Atomically confirms secret, but only if it is STILL the one awaiting
confirmation (the pending secret of a confirmed enrolment, else the secret
of an unconfirmed one): it becomes the active, confirmed secret, the pending
fields are cleared, and lastClaimedStep is raised to at least minStep.
Returns false, changing nothing, when another secret was staged meanwhile —
the code the caller checked belongs to a secret that is no longer awaiting,
and confirming the new one on its strength would confirm a secret nobody
proved.
Atomically consumes a recovery code by its SHA-256 digest.
Finds the index matching digest and marks it consumed in one operation,
so two concurrent uses of the same code cannot both succeed. Returns
true when the code was consumed, false when no matching digest exists
or it was already consumed.
The principal
The SHA-256 hex digest of the recovery code
Deletes the enrolment for a principal, including any recovery codes and attempt counts.
Reads the enrolment for a principal, or null when none exists.
Atomically reserves an attempt for lockout.
Counts the attempt and answers whether it is within the limit in one operation. Attempts are counted BEFORE the code is checked, not failures after it: a read-the-count-then-record-a-failure pair lets N concurrent guesses all read a count under the limit, so a burst would bypass the lockout.
An attempt refused as over the limit is NOT recorded. Recording it would let
an attacker who keeps guessing hold the account locked indefinitely — the
window would never clear — and would grow the stored count without bound.
Only allowed attempts count, so a lock lifts windowMs after the oldest
counted attempt, whatever arrives meanwhile.
The principal
The current wall-clock time in milliseconds
The limit and window
Stores recovery codes for a principal, replacing any existing set.
Codes are stored as SHA-256 digests (hex), not plaintext.
The principal
The SHA-256 hex digests of the recovery codes
Atomically stages secret as the one awaiting confirmation.
lastClaimedStep 0.secret/label.pendingSecret/pendingLabel, leaving the
confirmed secret in force.It NEVER writes lastClaimedStep or confirmed on an existing record. That
is the point of it being a single operation rather than a read followed by
a whole-record write: a write-back racing a concurrent claimStep would roll
the claimed step back and reopen replay of a code just used.
In-memory implementation of
ITotpStore.For tests and single-process development. All operations are synchronous internally but exposed through the async port so a remote backend can be substituted without a breaking change. No constructor arguments: the store needs no clock, because callers pass
nowtoITotpStore.reserveAttempt.