feat(mfa): forced TOTP enrolment at sign-in

This commit is contained in:
2026-09-16 08:41:56 +00:00
parent 2d75832ceb
commit 3fa469c303
3 changed files with 128 additions and 0 deletions
+33
View File
@@ -357,3 +357,36 @@ func burnTOTPCode(userID, code string) error {
}
return nil
}
// IssueRecoveryCodes replaces the user's set and returns the plaintext once.
// Callers must not persist or log the return value.
func IssueRecoveryCodes(instanceID, userID string) ([]string, error) {
plain, stored, err := GenerateRecoveryCodes()
if err != nil {
return nil, err
}
ctx, cancel := mfaCtx()
defer cancel()
_, err = db.Col("user_mfa").UpdateOne(ctx,
bson.M{"instance_id": instanceID, "user_id": userID},
bson.M{"$set": bson.M{"recovery_codes": stored, "updated_at": time.Now()}},
options.UpdateOne().SetUpsert(true))
if err != nil {
return nil, err
}
return plain, nil
}
// RecoveryCodesRemaining counts unused codes for the account page.
func RecoveryCodesRemaining(m *models.UserMFA) int {
if m == nil {
return 0
}
n := 0
for _, c := range m.RecoveryCodes {
if c.UsedAt == nil {
n++
}
}
return n
}