@@ -21,7 +21,6 @@ import (
21
21
"crypto/rand"
22
22
"encoding/base32"
23
23
"io"
24
- "math"
25
24
"net/url"
26
25
"strconv"
27
26
"time"
@@ -33,9 +32,9 @@ import (
33
32
34
33
// Validate a TOTP using the current time.
35
34
// A shortcut for ValidateCustom, Validate uses a configuration
36
- // that is compatible with Google-Authenticator and most clients.
37
- func Validate (passcode string , secret string ) bool {
38
- rv , _ := ValidateCustom (
35
+ // that is compatible with Google-Authenticator and most clients. See also ValidateStep.
36
+ func Validate (passcode string , secret string ) ( valid bool ) {
37
+ valid , _ , _ = ValidateCustomStep (
39
38
passcode ,
40
39
secret ,
41
40
time .Now ().UTC (),
@@ -46,7 +45,28 @@ func Validate(passcode string, secret string) bool {
46
45
Algorithm : otp .AlgorithmSHA1 ,
47
46
},
48
47
)
49
- return rv
48
+
49
+ return valid
50
+ }
51
+
52
+ // ValidateStep a TOTP using the current time.
53
+ // A shortcut for ValidateCustomStep, ValidateStep uses a configuration
54
+ // that is compatible with Google-Authenticator and most clients. This function is very similar to Validate except
55
+ // Validate does not return the step. The step can be used to safely record the code used by a user to prevent replay.
56
+ func ValidateStep (passcode string , secret string ) (valid bool , step uint64 ) {
57
+ valid , step , _ = ValidateCustomStep (
58
+ passcode ,
59
+ secret ,
60
+ time .Now ().UTC (),
61
+ ValidateOpts {
62
+ Period : 30 ,
63
+ Skew : 1 ,
64
+ Digits : otp .DigitsSix ,
65
+ Algorithm : otp .AlgorithmSHA1 ,
66
+ },
67
+ )
68
+
69
+ return valid , step
50
70
}
51
71
52
72
// GenerateCode creates a TOTP token using the current time.
@@ -82,7 +102,8 @@ func GenerateCodeCustom(secret string, t time.Time, opts ValidateOpts) (passcode
82
102
if opts .Period == 0 {
83
103
opts .Period = 30
84
104
}
85
- counter := uint64 (math .Floor (float64 (t .Unix ()) / float64 (opts .Period )))
105
+
106
+ counter := uint64 (t .Unix ()) / uint64 (opts .Period )
86
107
passcode , err = hotp .GenerateCodeCustom (secret , counter , hotp.ValidateOpts {
87
108
Digits : opts .Digits ,
88
109
Algorithm : opts .Algorithm ,
@@ -94,37 +115,43 @@ func GenerateCodeCustom(secret string, t time.Time, opts ValidateOpts) (passcode
94
115
}
95
116
96
117
// ValidateCustom validates a TOTP given a user specified time and custom options.
97
- // Most users should use Validate() to provide an interpolatable TOTP experience.
98
- func ValidateCustom (passcode string , secret string , t time.Time , opts ValidateOpts ) (bool , error ) {
118
+ // Most users should use Validate to provide an interpolatable TOTP experience.
119
+ func ValidateCustom (passcode string , secret string , t time.Time , opts ValidateOpts ) (valid bool , err error ) {
120
+ valid , _ , err = ValidateCustomStep (passcode , secret , t , opts )
121
+
122
+ return valid , err
123
+ }
124
+
125
+ // ValidateCustomStep validates a TOTP given a user specified time and custom options.
126
+ // Most users should use ValidateStep to provide an interpolatable TOTP experience.
127
+ func ValidateCustomStep (passcode string , secret string , t time.Time , opts ValidateOpts ) (valid bool , step uint64 , err error ) {
99
128
if opts .Period == 0 {
100
129
opts .Period = 30
101
130
}
102
131
103
- counters := []uint64 {}
104
- counter := int64 (math .Floor (float64 (t .Unix ()) / float64 (opts .Period )))
132
+ steps := []uint64 {uint64 (t .Unix ()) / uint64 (opts .Period )}
105
133
106
- counters = append (counters , uint64 (counter ))
107
- for i := 1 ; i <= int (opts .Skew ); i ++ {
108
- counters = append (counters , uint64 (counter + int64 (i )))
109
- counters = append (counters , uint64 (counter - int64 (i )))
134
+ for i := uint64 (1 ); i <= uint64 (opts .Skew ); i ++ {
135
+ steps = append (steps , steps [0 ]+ i )
136
+ steps = append (steps , steps [0 ]- i )
110
137
}
111
138
112
- for _ , counter := range counters {
113
- rv , err := hotp .ValidateCustom (passcode , counter , secret , hotp.ValidateOpts {
139
+ for _ , currentStep := range steps {
140
+ rv , err := hotp .ValidateCustom (passcode , currentStep , secret , hotp.ValidateOpts {
114
141
Digits : opts .Digits ,
115
142
Algorithm : opts .Algorithm ,
116
143
})
117
144
118
145
if err != nil {
119
- return false , err
146
+ return false , 0 , err
120
147
}
121
148
122
149
if rv == true {
123
- return true , nil
150
+ return true , currentStep , nil
124
151
}
125
152
}
126
153
127
- return false , nil
154
+ return false , 0 , nil
128
155
}
129
156
130
157
// GenerateOpts provides options for Generate(). The default values
0 commit comments