Skip to content

Commit 0f8bfbd

Browse files
feat(totp): overload which returns the step
This is adds an overload which allows returning the step. This is useful for validating the user has not reused a code.
1 parent 6d80748 commit 0f8bfbd

File tree

4 files changed

+59
-31
lines changed

4 files changed

+59
-31
lines changed

example/main.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
11
package main
22

33
import (
4-
"github.com/authelia/otp"
5-
"github.com/authelia/otp/totp"
6-
74
"bufio"
85
"bytes"
96
"encoding/base32"
107
"fmt"
118
"image/png"
12-
"io/ioutil"
139
"os"
1410
"time"
11+
12+
"github.com/authelia/otp"
13+
"github.com/authelia/otp/totp"
1514
)
1615

1716
func display(key *otp.Key, data []byte) {
1817
fmt.Printf("Issuer: %s\n", key.Issuer())
1918
fmt.Printf("Account Name: %s\n", key.AccountName())
2019
fmt.Printf("Secret: %s\n", key.Secret())
2120
fmt.Println("Writing PNG to qr-code.png....")
22-
ioutil.WriteFile("qr-code.png", data, 0644)
21+
os.WriteFile("qr-code.png", data, 0644)
2322
fmt.Println("")
2423
fmt.Println("Please add your TOTP to your OTP Application now!")
2524
fmt.Println("")

hotp/hotp_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@
1818
package hotp
1919

2020
import (
21-
"github.com/authelia/otp"
21+
"encoding/base32"
22+
"testing"
23+
2224
"github.com/stretchr/testify/assert"
2325
"github.com/stretchr/testify/require"
2426

25-
"encoding/base32"
26-
"testing"
27+
"github.com/authelia/otp"
2728
)
2829

2930
type tc struct {

totp/totp.go

+46-19
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"crypto/rand"
2222
"encoding/base32"
2323
"io"
24-
"math"
2524
"net/url"
2625
"strconv"
2726
"time"
@@ -33,9 +32,9 @@ import (
3332

3433
// Validate a TOTP using the current time.
3534
// 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(
3938
passcode,
4039
secret,
4140
time.Now().UTC(),
@@ -46,7 +45,28 @@ func Validate(passcode string, secret string) bool {
4645
Algorithm: otp.AlgorithmSHA1,
4746
},
4847
)
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
5070
}
5171

5272
// GenerateCode creates a TOTP token using the current time.
@@ -82,7 +102,8 @@ func GenerateCodeCustom(secret string, t time.Time, opts ValidateOpts) (passcode
82102
if opts.Period == 0 {
83103
opts.Period = 30
84104
}
85-
counter := uint64(math.Floor(float64(t.Unix()) / float64(opts.Period)))
105+
106+
counter := uint64(t.Unix()) / uint64(opts.Period)
86107
passcode, err = hotp.GenerateCodeCustom(secret, counter, hotp.ValidateOpts{
87108
Digits: opts.Digits,
88109
Algorithm: opts.Algorithm,
@@ -94,37 +115,43 @@ func GenerateCodeCustom(secret string, t time.Time, opts ValidateOpts) (passcode
94115
}
95116

96117
// 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) {
99128
if opts.Period == 0 {
100129
opts.Period = 30
101130
}
102131

103-
counters := []uint64{}
104-
counter := int64(math.Floor(float64(t.Unix()) / float64(opts.Period)))
132+
steps := []uint64{uint64(t.Unix()) / uint64(opts.Period)}
105133

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)
110137
}
111138

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{
114141
Digits: opts.Digits,
115142
Algorithm: opts.Algorithm,
116143
})
117144

118145
if err != nil {
119-
return false, err
146+
return false, 0, err
120147
}
121148

122149
if rv == true {
123-
return true, nil
150+
return true, currentStep, nil
124151
}
125152
}
126153

127-
return false, nil
154+
return false, 0, nil
128155
}
129156

130157
// GenerateOpts provides options for Generate(). The default values

totp/totp_test.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818
package totp
1919

2020
import (
21-
"github.com/authelia/otp"
22-
"github.com/stretchr/testify/assert"
23-
"github.com/stretchr/testify/require"
24-
2521
"encoding/base32"
2622
"testing"
2723
"time"
24+
25+
"github.com/stretchr/testify/assert"
26+
"github.com/stretchr/testify/require"
27+
28+
"github.com/authelia/otp"
2829
)
2930

3031
type tc struct {

0 commit comments

Comments
 (0)