package auth import ( "crypto/rand" "encoding/hex" "errors" "fmt" "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" "golang.org/x/crypto/bcrypt" "net/http" "strings" "time" ) type TokenType string const ( // TokenTypeAccess - TokenTypeAccess TokenType = "chirpy-access" ) func HashPassword(password string) (string, error) { pass, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) return string(pass), err } func CheckPasswordHash(password, hash string) error { return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) } // MakeJWT - func MakeJWT( userID uuid.UUID, tokenSecret string, expiresIn time.Duration, ) (string, error) { signingKey := []byte(tokenSecret) token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{ Issuer: string(TokenTypeAccess), IssuedAt: jwt.NewNumericDate(time.Now().UTC()), ExpiresAt: jwt.NewNumericDate(time.Now().UTC().Add(expiresIn)), Subject: userID.String(), }) return token.SignedString(signingKey) } // ValidateJWT - func ValidateJWT(tokenString, tokenSecret string) (uuid.UUID, error) { claimsStruct := jwt.RegisteredClaims{} token, err := jwt.ParseWithClaims( tokenString, &claimsStruct, func(token *jwt.Token) (interface{}, error) { return []byte(tokenSecret), nil }, ) if err != nil { return uuid.Nil, err } userIDString, err := token.Claims.GetSubject() if err != nil { return uuid.Nil, err } issuer, err := token.Claims.GetIssuer() if err != nil { return uuid.Nil, err } if issuer != string(TokenTypeAccess) { return uuid.Nil, errors.New("invalid issuer") } id, err := uuid.Parse(userIDString) if err != nil { return uuid.Nil, fmt.Errorf("invalid user ID: %w", err) } return id, nil } func GetBearerToken(headers http.Header) (string, error) { auth := headers.Get("Authorization") auth = strings.TrimPrefix(auth, "Bearer ") if auth == "" { return "", errors.New("missing Authorization header") } return auth, nil } func MakeRefreshToken() (string, error) { key := make([]byte, 32) _, err := rand.Read(key) if err != nil { return "", err } token := hex.EncodeToString(key) return token, nil } func GetAPIKey(headers http.Header) (string, error) { auth := headers.Get("Authorization") auth = strings.TrimPrefix(auth, "ApiKey ") if auth == "" { return "", errors.New("missing Authorization header") } return auth, nil }