...
1 package totp
2
3 import (
4 "encoding/base32"
5 "errors"
6 "fmt"
7 "time"
8
9 "edge-infra.dev/pkg/edge/api/graph/model"
10
11 "github.com/pquerna/otp"
12 "github.com/pquerna/otp/totp"
13 )
14
15 const (
16
17
18 DefaultTokenDuration = 600
19
20
21 DefaultOtpLength = 6
22
23 InvalidTOTPToken = "invalid totp token/secret"
24 )
25
26 var (
27 ErrInvalidTOTPToken = errors.New(InvalidTOTPToken)
28 )
29
30
31 func GenerateTotp(totpSecret string) (*model.Totp, error) {
32 secret := base32.StdEncoding.EncodeToString([]byte(totpSecret))
33 currentTime := time.Now().UTC()
34 expiryTime := currentTime.Add(DefaultTokenDuration * time.Second)
35 passcode, err := totp.GenerateCodeCustom(secret, currentTime, totp.ValidateOpts{
36 Period: DefaultTokenDuration,
37 Digits: otp.Digits(DefaultOtpLength),
38 })
39 if err != nil {
40 return nil, err
41 }
42 response := &model.Totp{
43 Code: passcode,
44 CreatedAt: currentTime.String(),
45 ExpiresAt: expiryTime.String(),
46 Duration: DefaultTokenDuration,
47 }
48 return response, nil
49 }
50
51
52 func ValidateTotpToken(token string, totpSecret string) error {
53 secret := base32.StdEncoding.EncodeToString([]byte(totpSecret))
54 currentTime := time.Now().UTC()
55 valid, err := totp.ValidateCustom(token, secret, currentTime, totp.ValidateOpts{
56 Period: DefaultTokenDuration,
57 Digits: otp.Digits(DefaultOtpLength),
58 })
59 if err != nil {
60 return fmt.Errorf("%s: %v", InvalidTOTPToken, err)
61 }
62 if !valid {
63 return ErrInvalidTOTPToken
64 }
65 return nil
66 }
67
View as plain text