/
token.go
68 lines (59 loc) · 2.44 KB
/
token.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
package models
import (
"crypto/rand"
"crypto/sha256"
"encoding/base32"
"time"
)
// Define constants for the token scope. For now we just define the scope "activation"
// but we'll add additional scopes later in the book.
const (
ScopeActivation = "activation"
ScopeAuthentication = "authentication"
)
// Define a Token struct to hold the data for an individual token. This includes the
// plaintext and hashed versions of the token, associated user ID, expiry time and
// scope.
type Token struct {
Plaintext string `json:"token"`
Hash []byte `json:"-" db:hash`
UserID int64 `json:"-" db:user_id`
Expiry time.Time `json:"expiry" db:expiry`
Scope string `json:"-" db:scope`
}
func GenerateToken(userID int64, ttl time.Duration, scope string) (*Token, error) {
// Create a Token instance containing the user ID, expiry, and scope information.
// Notice that we add the provided ttl (time-to-live) duration parameter to the
// current time to get the expiry time?
token := &Token{
UserID: userID,
Expiry: time.Now().Add(ttl),
Scope: scope,
}
// Initialize a zero-valued byte slice with a length of 16 bytes.
randomBytes := make([]byte, 16)
// Use the Read() function from the crypto/rand package to fill the byte slice with
// random bytes from your operating system's CSPRNG. This will return an error if
// the CSPRNG fails to function correctly.
_, err := rand.Read(randomBytes)
if err != nil {
return nil, err
}
// Encode the byte slice to a base-32-encoded string and assign it to the token
// Plaintext field. This will be the token string that we send to the user in their
// welcome email. They will look similar to this:
//
// Y3QMGX3PJ3WLRL2YRTQGQ6KRHU
//
// Note that by default base-32 strings may be padded at the end with the =
// character. We don't need this padding character for the purpose of our tokens, so
// we use the WithPadding(base32.NoPadding) method in the line below to omit them.
token.Plaintext = base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(randomBytes)
// Generate a SHA-256 hash of the plaintext token string. This will be the value
// that we store in the `hash` field of our database table. Note that the
// sha256.Sum256() function returns an *array* of length 32, so to make it easier to
// work with we convert it to a slice using the [:] operator before storing it.
hash := sha256.Sum256([]byte(token.Plaintext))
token.Hash = hash[:]
return token, nil
}