feat(mfa): forced TOTP enrolment at sign-in
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user