mirror of
https://github.com/Awuqing/BackupX.git
synced 2026-08-28 19:47:27 +08:00
feat: add complete MFA support
Add complete MFA support with TOTP, recovery codes, WebAuthn, trusted-device cookie flow, and email/SMS OTP delivery via notification channels. Security follow-up: trusted device tokens are stored in HttpOnly cookies, and SMS OTP reuses the existing Webhook notifier to avoid introducing a new dynamic URL sink.
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
package security
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"image/png"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
)
|
||||
|
||||
const TOTPIssuer = "BackupX"
|
||||
|
||||
type TOTPEnrollment struct {
|
||||
Secret string
|
||||
OTPAuthURL string
|
||||
QRCodeDataURL string
|
||||
}
|
||||
|
||||
func GenerateTOTPEnrollment(accountName string) (*TOTPEnrollment, error) {
|
||||
key, err := totp.Generate(totp.GenerateOpts{
|
||||
Issuer: TOTPIssuer,
|
||||
AccountName: accountName,
|
||||
Period: 30,
|
||||
SecretSize: 20,
|
||||
Digits: otp.DigitsSix,
|
||||
Algorithm: otp.AlgorithmSHA1,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
image, err := key.Image(220, 220)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := png.Encode(&buf, image); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &TOTPEnrollment{
|
||||
Secret: key.Secret(),
|
||||
OTPAuthURL: key.URL(),
|
||||
QRCodeDataURL: "data:image/png;base64," + base64.StdEncoding.EncodeToString(buf.Bytes()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ValidateTOTPCode(secret string, code string) (bool, error) {
|
||||
return totp.ValidateCustom(NormalizeTOTPCode(code), secret, time.Now().UTC(), totp.ValidateOpts{
|
||||
Period: 30,
|
||||
Skew: 1,
|
||||
Digits: otp.DigitsSix,
|
||||
Algorithm: otp.AlgorithmSHA1,
|
||||
})
|
||||
}
|
||||
|
||||
func NormalizeTOTPCode(code string) string {
|
||||
return strings.Map(func(r rune) rune {
|
||||
if unicode.IsSpace(r) {
|
||||
return -1
|
||||
}
|
||||
return r
|
||||
}, strings.TrimSpace(code))
|
||||
}
|
||||
Reference in New Issue
Block a user