package auth import ( "fmt" "github.com/pquerna/otp" "github.com/pquerna/otp/totp" ) const totpIssuer = "ClusterCanvas" // GenerateTOTPSecret creates a new TOTP key for username. func GenerateTOTPSecret(username string) (*otp.Key, error) { key, err := totp.Generate(totp.GenerateOpts{ Issuer: totpIssuer, AccountName: username, Period: 30, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA1, }) if err != nil { return nil, fmt.Errorf("generate totp: %w", err) } return key, nil } // VerifyTOTPCode validates a TOTP code against a base32 secret. func VerifyTOTPCode(secret string, code string) bool { return totp.Validate(code, secret) } // OTPAuthURL builds an otpauth URL from an existing secret. func OTPAuthURL(username string, secret string) (string, error) { key, err := otp.NewKeyFromURL( fmt.Sprintf( "otpauth://totp/%s:%s?secret=%s&issuer=%s&algorithm=SHA1&digits=6&period=30", totpIssuer, username, secret, totpIssuer, ), ) if err != nil { return "", err } return key.URL(), nil }