package auth import ( "context" "crypto/rand" "crypto/sha256" "encoding/hex" "errors" "fmt" "time" "github.com/golang-jwt/jwt/v5" "golang.org/x/crypto/bcrypt" ) type contextKey string const UserIDContextKey contextKey = "mindsift_user_id" type Claims struct { UserID string `json:"userId"` Email string `json:"email"` DisplayName string `json:"displayName"` jwt.RegisteredClaims } func HashPassword(password string) (string, error) { bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) return string(bytes), err } func CheckPassword(password, hash string) bool { err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) return err == nil } func GenerateToken(secret, userID, email, displayName string) (string, error) { claims := &Claims{ UserID: userID, Email: email, DisplayName: displayName, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(30 * 24 * time.Hour)), IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: "mindsift", }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(secret)) } func ValidateToken(secret, tokenStr string) (*Claims, error) { token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } return []byte(secret), nil }) if err != nil { return nil, err } claims, ok := token.Claims.(*Claims) if !ok || !token.Valid { return nil, errors.New("invalid token claims") } return claims, nil } // GenerateAPIKey creates a new webhook/ingestion API key (e.g. mindsift_sec_...) and returns rawKey, prefix, sha256Hash func GenerateAPIKey() (string, string, string) { b := make([]byte, 18) _, _ = rand.Read(b) raw := "mindsift_sec_" + hex.EncodeToString(b) prefix := raw[:20] + "..." return raw, prefix, HashAPIKey(raw) } func HashAPIKey(rawKey string) string { sum := sha256.Sum256([]byte(rawKey)) return hex.EncodeToString(sum[:]) } func WithUserID(ctx context.Context, userID string) context.Context { return context.WithValue(ctx, UserIDContextKey, userID) } func GetUserID(ctx context.Context) string { if val, ok := ctx.Value(UserIDContextKey).(string); ok { return val } return "" }