Files
chirpy/internal/auth/auth.go
T
Stevan Freeborn 9eca3c71e2 docs: document auth helpers and expand README
Add godoc comments to the exported auth package helpers. Expand the README
with feature and tech stack overviews, setup instructions, an API reference,
and make targets for development workflows.
2026-08-15 22:07:31 -05:00

102 lines
2.7 KiB
Go

// Package auth provides password hashing, JWT, and token helpers used to
// authenticate and authorize API requests.
package auth
import (
"crypto/rand"
"encoding/hex"
"errors"
"net/http"
"strings"
"time"
"github.com/alexedwards/argon2id"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
)
// HashPassword hashes a plaintext password using argon2id.
func HashPassword(password string) (string, error) {
return argon2id.CreateHash(password, argon2id.DefaultParams)
}
// CheckPasswordHash reports whether password matches the given argon2id hash.
func CheckPasswordHash(password string, hash string) (bool, error) {
return argon2id.ComparePasswordAndHash(password, hash)
}
// MakeJWT creates a signed HS256 JWT for the given user that expires after
// expiresIn.
func MakeJWT(userID uuid.UUID, tokenSecret []byte, expiresIn time.Duration) (string, error) {
token := jwt.New(jwt.GetSigningMethod(jwt.SigningMethodHS256.Name))
token.Claims = &jwt.RegisteredClaims{
Issuer: "chirpy-access",
IssuedAt: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(expiresIn)),
Subject: userID.String(),
}
return token.SignedString(tokenSecret)
}
// ValidateJWT verifies the token signature and returns the subject's user ID.
func ValidateJWT(tokenString string, tokenSecret []byte) (uuid.UUID, error) {
token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(t *jwt.Token) (any, error) {
return tokenSecret, nil
})
if err != nil {
return uuid.Nil, err
}
userId, err := token.Claims.GetSubject()
if err != nil {
return uuid.Nil, err
}
validUserId, err := uuid.Parse(userId)
if err != nil {
return uuid.Nil, err
}
return validUserId, nil
}
// GetBearerToken extracts the "Bearer" token from the Authorization header.
func GetBearerToken(headers http.Header) (string, error) {
authorizationHeader := headers.Get("Authorization")
if strings.TrimSpace(authorizationHeader) == "" {
return "", errors.New("Missing Authorization header")
}
token := strings.TrimPrefix(authorizationHeader, "Bearer ")
return token, nil
}
// GetAPIKey extracts the API key from the Authorization header using the
// "ApiKey" scheme.
func GetAPIKey(headers http.Header) (string, error) {
authorizationHeader := headers.Get("Authorization")
if strings.TrimSpace(authorizationHeader) == "" {
return "", errors.New("Missing Authorization header")
}
apiKey := strings.TrimPrefix(authorizationHeader, "ApiKey ")
return apiKey, nil
}
// MakeRefreshToken generates a cryptographically random, hex-encoded refresh
// token.
func MakeRefreshToken() string {
bytes := make([]byte, 32)
rand.Read(bytes)
return hex.EncodeToString(bytes)
}