@@ -2,6 +2,7 @@ package apikey
22
33import (
44"crypto/sha256"
5+ "crypto/subtle"
56"fmt"
67"net"
78"time"
@@ -44,12 +45,17 @@ type CreateParams struct {
4445// database representation. It is the responsibility of the caller to insert it
4546// into the database.
4647func Generate (params CreateParams ) (database.InsertAPIKeyParams ,string ,error ) {
47- keyID ,keySecret ,err := generateKey ()
48+ // Length of an API Key ID.
49+ keyID ,err := cryptorand .String (10 )
4850if err != nil {
49- return database.InsertAPIKeyParams {},"" ,xerrors .Errorf ("generate API key: %w" ,err )
51+ return database.InsertAPIKeyParams {},"" ,xerrors .Errorf ("generate API key ID : %w" ,err )
5052}
5153
52- hashed := sha256 .Sum256 ([]byte (keySecret ))
54+ // Length of an API Key secret.
55+ keySecret ,hashedSecret ,err := GenerateSecret (22 )
56+ if err != nil {
57+ return database.InsertAPIKeyParams {},"" ,xerrors .Errorf ("generate API key secret: %w" ,err )
58+ }
5359
5460// Default expires at to now+lifetime, or use the configured value if not
5561// set.
@@ -120,7 +126,7 @@ func Generate(params CreateParams) (database.InsertAPIKeyParams, string, error)
120126ExpiresAt :params .ExpiresAt .UTC (),
121127CreatedAt :dbtime .Now (),
122128UpdatedAt :dbtime .Now (),
123- HashedSecret :hashed [:],
129+ HashedSecret :hashedSecret [:],
124130LoginType :params .LoginType ,
125131Scopes :scopes ,
126132AllowList :params .AllowList ,
@@ -142,3 +148,25 @@ func generateKey() (id string, secret string, err error) {
142148}
143149return id ,secret ,nil
144150}
151+
152+ func GenerateSecret (length int ) (secret string ,hashed []byte ,err error ) {
153+ secret ,err = cryptorand .String (length )
154+ if err != nil {
155+ return "" ,nil ,err
156+ }
157+ hash := hashSecret (secret )
158+ return secret ,hash [:],nil
159+ }
160+
161+ // ValidateHash compares a secret against an expected hashed secret.
162+ func ValidateHash (hashedSecret []byte ,secret string )bool {
163+ hash := hashSecret (secret )
164+ return subtle .ConstantTimeCompare (hashedSecret ,hash [:])== 1
165+ }
166+
167+ // hashSecret is the single function used to hash API key secrets.
168+ // Use this to ensure a consistent hashing algorithm.
169+ func hashSecret (secret string ) []byte {
170+ hash := sha256 .Sum256 ([]byte (secret ))
171+ return hash [:]
172+ }