Skip to content

TwoFactorRecoveryCode ​

One single-use recovery code, accepted by VerifyAsync in place of a TOTP code when a user has lost their authenticator.

Codes are issued as a set by CompleteEnrolmentAsync and returned in plain text exactly once. One row per code means spending one is a single update rather than a rewrite of the whole set.

DANGER

TwoFactorRecoveryCode is a database entity. Never return it from an endpoint: it carries the code hash and the surrogate keys. Map it to a DTO that exposes only the fields the client needs.

Usage ​

csharp
using Microsoft.EntityFrameworkCore;
using AlmightyShogun.AspNet.Auth.Credentials;

public sealed class RecoveryCodeCounter(AppDbContext database)
{
    public Task<int> CountRemainingAsync(int enrolmentId)
        => database.TwoFactorRecoveryCodes
            .Where(code => code.UsedAt == null)
            .CountAsync(code => code.UserTwoFactorId == enrolmentId);
}

Fields

Id: int
The surrogate key of the recovery code row.

UserTwoFactorId: int
The enrolment the code belongs to. Cascades with the enrolment, so disabling two-factor removes the codes with it.

CodeHash: string
Hash of the code that was shown to the user. The code itself is never stored, so a lost set cannot be recovered and must be reissued.

UsedAt: DateTimeOffset?
When the code was spent, or null while it is still usable. Set instead of deleting the row, so a replayed code is recognised rather than looking unknown.
Default: null

All packages are released under the MIT License.