package authkit import ( "crypto/sha256" "crypto/subtle" "encoding/hex" "net/http" "strings" ) // ExtractAPIKey pulls the API key from r. // Checks X-API-Key first, then "Authorization: Bearer ". // Returns "" if neither is present. func ExtractAPIKey(r *http.Request) string { if v := strings.TrimSpace(r.Header.Get("X-API-Key")); v != "" { return v } auth := strings.TrimSpace(r.Header.Get("Authorization")) if strings.HasPrefix(auth, "Bearer ") { return strings.TrimSpace(auth[len("Bearer "):]) } return "" } // ConstantTimeEqualString compares a and b in constant time. func ConstantTimeEqualString(a, b string) bool { if len(a) != len(b) { return false } return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1 } // ConstantTimeEqualHashedKey hashes candidatePlain with sha256 and compares // against storedHashHex in constant time. Returns false on bad hex; never panics. func ConstantTimeEqualHashedKey(candidatePlain, storedHashHex string) bool { stored, err := hex.DecodeString(storedHashHex) if err != nil || len(stored) != sha256.Size { return false } sum := sha256.Sum256([]byte(candidatePlain)) return subtle.ConstantTimeCompare(sum[:], stored) == 1 }