-
Notifications
You must be signed in to change notification settings - Fork 610
/
secrets.go
77 lines (67 loc) · 2.18 KB
/
secrets.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
package identityprovider
import (
"fmt"
"strings"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/coderd/userpassword"
"github.com/coder/coder/v2/cryptorand"
)
type OAuth2ProviderAppSecret struct {
// Formatted contains the secret. This value is owned by the client, not the
// server. It is formatted to include the prefix.
Formatted string
// Prefix is the ID of this secret owned by the server. When a client uses a
// secret, this is the matching string to do a lookup on the hashed value. We
// cannot use the hashed value directly because the server does not store the
// salt.
Prefix string
// Hashed is the server stored hash(secret,salt,...). Used for verifying a
// secret.
Hashed string
}
// GenerateSecret generates a secret to be used as a client secret, refresh
// token, or authorization code.
func GenerateSecret() (OAuth2ProviderAppSecret, error) {
// 40 characters matches the length of GitHub's client secrets.
secret, err := cryptorand.String(40)
if err != nil {
return OAuth2ProviderAppSecret{}, err
}
// This ID is prefixed to the secret so it can be used to look up the secret
// when the user provides it, since we cannot just re-hash it to match as we
// will not have the salt.
prefix, err := cryptorand.String(10)
if err != nil {
return OAuth2ProviderAppSecret{}, err
}
hashed, err := userpassword.Hash(secret)
if err != nil {
return OAuth2ProviderAppSecret{}, err
}
return OAuth2ProviderAppSecret{
Formatted: fmt.Sprintf("coder_%s_%s", prefix, secret),
Prefix: prefix,
Hashed: hashed,
}, nil
}
type parsedSecret struct {
prefix string
secret string
}
// parseSecret extracts the ID and original secret from a secret.
func parseSecret(secret string) (parsedSecret, error) {
parts := strings.Split(secret, "_")
if len(parts) != 3 {
return parsedSecret{}, xerrors.Errorf("incorrect number of parts: %d", len(parts))
}
if parts[0] != "coder" {
return parsedSecret{}, xerrors.Errorf("incorrect scheme: %s", parts[0])
}
if len(parts[1]) == 0 {
return parsedSecret{}, xerrors.Errorf("prefix is invalid")
}
if len(parts[2]) == 0 {
return parsedSecret{}, xerrors.Errorf("invalid")
}
return parsedSecret{parts[1], parts[2]}, nil
}