// Package auth provides password hashing and opaque session tokens. package auth import ( "crypto/rand" "crypto/sha256" "encoding/base64" "encoding/hex" "fmt" "golang.org/x/crypto/bcrypt" ) // HashPassword returns a bcrypt hash of password. func HashPassword(password string) (string, error) { hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return "", fmt.Errorf("hash password: %w", err) } return string(hash), nil } // CheckPassword reports whether password matches the stored hash. func CheckPassword(hash, password string) bool { return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil } // NewToken returns a random 256-bit hex string for a session cookie. func NewToken() (string, error) { buf := make([]byte, 32) if _, err := rand.Read(buf); err != nil { return "", fmt.Errorf("generate token: %w", err) } return hex.EncodeToString(buf), nil } // apiTokenPrefix marks git tokens and tells them apart from passwords in // the HTTP basic-auth password field. const apiTokenPrefix = "sg_" // NewAPIToken returns a fresh git token: the prefix plus 32 random bytes. func NewAPIToken() (string, error) { buf := make([]byte, 32) if _, err := rand.Read(buf); err != nil { return "", fmt.Errorf("generate api token: %w", err) } return apiTokenPrefix + base64.RawURLEncoding.EncodeToString(buf), nil } // HashToken returns the hex SHA-256 digest used to store and look up a git // token. A fast digest is correct here: the token is high-entropy, so there // is nothing to brute-force, and an indexed digest lookup keeps auth O(1). func HashToken(token string) string { sum := sha256.Sum256([]byte(token)) return hex.EncodeToString(sum[:]) } // TokenHint is the non-secret prefix shown in the token list. func TokenHint(token string) string { if len(token) <= 8 { return token } return token[:8] }