Setu-TS
    Preparing search index...

    Interface ITotpStore

    Store port for TOTP enrolment, step claims, lockout, and recovery codes.

    All methods are async so remote backends can implement the interface without a breaking change.

    interface ITotpStore {
        claimStep(principalId: string, step: number): Promise<boolean>;
        clearAttempts(principalId: string): Promise<void>;
        confirmSecret(
            principalId: string,
            secret: string,
            minStep: number,
        ): Promise<boolean>;
        consumeRecoveryCode(principalId: string, digest: string): Promise<boolean>;
        deleteEnrolment(principalId: string): Promise<void>;
        getEnrolment(principalId: string): Promise<TotpEnrolment | null>;
        reserveAttempt(
            principalId: string,
            now: number,
            options: { limit: number; windowMs: number },
        ): Promise<ReserveAttemptResult>;
        saveRecoveryCodes(
            principalId: string,
            digests: readonly string[],
        ): Promise<void>;
        stageSecret(
            principalId: string,
            secret: string,
            label: string,
        ): Promise<void>;
    }

    Implemented by

    Index
    • 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.

      Parameters

      • principalId: string
      • step: number

      Returns Promise<boolean>

    • 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.

      Parameters

      • principalId: string
      • secret: string
      • minStep: number

      Returns Promise<boolean>

    • 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.

      Parameters

      • principalId: string

        The principal

      • digest: string

        The SHA-256 hex digest of the recovery code

      Returns Promise<boolean>

    • Deletes the enrolment for a principal, including any recovery codes and attempt counts.

      Parameters

      • principalId: string

      Returns Promise<void>

    • 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.

      Parameters

      • principalId: string

        The principal

      • now: number

        The current wall-clock time in milliseconds

      • options: { limit: number; windowMs: number }

        The limit and window

      Returns Promise<ReserveAttemptResult>

    • Stores recovery codes for a principal, replacing any existing set.

      Codes are stored as SHA-256 digests (hex), not plaintext.

      Parameters

      • principalId: string

        The principal

      • digests: readonly string[]

        The SHA-256 hex digests of the recovery codes

      Returns Promise<void>

    • Atomically stages secret as the one awaiting confirmation.

      • No enrolment: creates an unconfirmed one with lastClaimedStep 0.
      • An unconfirmed enrolment: replaces its secret/label.
      • A confirmed enrolment: sets 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.

      Parameters

      • principalId: string
      • secret: string
      • label: string

      Returns Promise<void>