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