/
generateotp.go
101 lines (91 loc) · 2.84 KB
/
generateotp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package commands
import (
"crypto/rand"
"crypto/sha256"
"fmt"
"math/big"
"time"
"github.com/Goldwin/ies-pik-cms/pkg/auth/dto"
"github.com/Goldwin/ies-pik-cms/pkg/auth/entities"
. "github.com/Goldwin/ies-pik-cms/pkg/common/commands"
)
type GenerateOtpCommand struct {
Email string
TTLMillis int64
}
const (
GenerateOtpErrorInvalidEmail CommandErrorCode = 20001
GenerateOtpErrorFailedToGenOtp CommandErrorCode = 20002
GenerateOtpErrorFailedToStoreOtp CommandErrorCode = 20003
GenerateOtpErrorOtpExists CommandErrorCode = 20004
)
func (cmd GenerateOtpCommand) Execute(ctx CommandContext) CommandExecutionResult[dto.OtpResult] {
otp, _ := ctx.OtpRepository().GetOtp(entities.EmailAddress(cmd.Email))
if otp != nil {
expireSecond := otp.ExpiredTime.Sub(time.Now()).Seconds()
if expireSecond > 0 {
return CommandExecutionResult[dto.OtpResult]{
Status: ExecutionStatusFailed,
Error: CommandErrorDetail{
Code: GenerateOtpErrorOtpExists,
Message: fmt.Sprintf("OTP Already Exists. Please wait for %.0f seconds and try again.", expireSecond),
},
}
}
}
//30 seconds minimum
ttlMillis := max(cmd.TTLMillis, 30000)
password, err := rand.Int(rand.Reader, big.NewInt(999999))
if err != nil {
return CommandExecutionResult[dto.OtpResult]{
Status: ExecutionStatusFailed,
Error: CommandErrorDetail{
Code: GenerateOtpErrorFailedToGenOtp,
Message: fmt.Sprintf("Failed to Generate OTP: %s", err.Error()),
},
}
}
salt, err := rand.Int(rand.Reader, big.NewInt(999999))
if err != nil {
return CommandExecutionResult[dto.OtpResult]{
Status: ExecutionStatusFailed,
Error: CommandErrorDetail{
Code: GenerateOtpErrorFailedToGenOtp,
Message: fmt.Sprintf("Failed to Generate OTP's Salt: %s", err.Error()),
},
}
}
n := password.Int64()
passwordBytes := []byte(fmt.Sprintf("%06v", n))
passwordAndSalt := append(passwordBytes, salt.Bytes()...)
passwordHash := sha256.Sum256(passwordAndSalt)
result := entities.Otp{
EmailAddress: entities.EmailAddress(cmd.Email),
PasswordHash: passwordHash[:],
Salt: salt.Bytes(),
ExpiredTime: time.Now().Add(time.Duration(ttlMillis) * time.Millisecond),
}
if !result.EmailAddress.IsValid() {
return CommandExecutionResult[dto.OtpResult]{
Status: ExecutionStatusFailed,
Error: CommandErrorDetail{
Code: GenerateOtpErrorInvalidEmail,
Message: "Invalid Email Address",
},
}
}
err = ctx.OtpRepository().AddOtp(result)
if err != nil {
return CommandExecutionResult[dto.OtpResult]{
Status: ExecutionStatusFailed,
Error: CommandErrorDetail{
Code: GenerateOtpErrorFailedToStoreOtp,
Message: fmt.Sprintf("Failed to Generate OTP: %s", err.Error()),
},
}
}
return CommandExecutionResult[dto.OtpResult]{Status: ExecutionStatusSuccess, Result: dto.OtpResult{
Email: cmd.Email,
OTP: passwordBytes,
}}
}