b8622ddd60a831ec5e82333e8c47f4706d65253e / internal/auth/auth.go · 1904 bytes · raw
// 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]
}