Documentation
¶
Overview ¶
Package auth provides authentication and authorization for Hector v2.
This package is ported from legacy Hector (pkg/auth) and adapted to work with a2a-go's CallInterceptor system for seamless integration.
Architecture ¶
The auth package follows a layered approach:
- JWTValidator: Validates JWT tokens using JWKS (JSON Web Key Set)
- HTTP Middleware: Extracts and validates tokens from HTTP requests
- CallInterceptor: Bridges to a2a-go's authentication system
Usage ¶
Configure authentication in your hector.yaml:
server:
auth:
enabled: true
jwks_url: "https://auth.example.com/.well-known/jwks.json"
issuer: "https://auth.example.com"
audience: "hector-api"
The auth middleware will automatically validate JWT tokens and make claims available to agents via the invocation context.
Index ¶
- Constants
- Variables
- func ContextWithClaims(ctx context.Context, claims *Claims) context.Context
- func GenerateDeterministicEd25519KeyPEM(seed string) ([]byte, error)
- func GenerateDeterministicRSAPrivateKeyPEM(seed string) ([]byte, error)
- func LoadOrGenerateRSAPrivateKey(path string) ([]byte, error)
- func Middleware(validator TokenValidator) func(http.Handler) http.Handler
- func MiddlewareWithExclusions(validator TokenValidator, excludedPaths []string) func(http.Handler) http.Handler
- func OptionalMiddleware(validator TokenValidator) func(http.Handler) http.Handler
- func RequireRole(roles ...string) func(http.Handler) http.Handler
- func RequireTenant(tenants ...string) func(http.Handler) http.Handler
- type AuthenticatedUser
- type Claims
- type CompositeValidator
- type CredentialType
- type DeterministicReader
- type Interceptor
- type JWTValidator
- type JWTValidatorConfig
- type MultiIssuerConfig
- type MultiIssuerValidator
- type SecretValidator
- type TokenIssuer
- func (i *TokenIssuer) Close() error
- func (i *TokenIssuer) IssueAdminToken(subject string, ttl time.Duration) (string, error)
- func (i *TokenIssuer) IssueToken(appID string, ttl time.Duration) (string, error)
- func (i *TokenIssuer) Issuer() string
- func (i *TokenIssuer) PublicJWKS() jwk.Set
- func (i *TokenIssuer) ValidateToken(ctx context.Context, tokenString string) (*Claims, error)
- type TokenIssuerConfig
- type TokenProvider
- type TokenValidator
Constants ¶
const (
// ClaimsContextKey is the context key for storing validated claims.
ClaimsContextKey contextKey = "hector_auth_claims"
)
Variables ¶
var ( ErrUnauthorized = errors.New("unauthorized: authentication required") // ErrForbidden is returned when the user lacks permission. ErrForbidden = errors.New("forbidden: insufficient permissions") // ErrInvalidToken is returned when a token cannot be validated. ErrInvalidToken = errors.New("invalid token") // ErrTokenExpired is returned when a token has expired. ErrTokenExpired = errors.New("token expired") // ErrMissingClaims is returned when required claims are missing. ErrMissingClaims = errors.New("missing required claims") )
Common authentication errors.
Functions ¶
func ContextWithClaims ¶
ContextWithClaims returns a new context with the given claims.
func GenerateDeterministicEd25519KeyPEM ¶ added in v1.21.0
GenerateDeterministicEd25519KeyPEM generates an Ed25519 private key deterministically from a seed.
func GenerateDeterministicRSAPrivateKeyPEM ¶ added in v1.21.0
GenerateDeterministicRSAPrivateKeyPEM generates an RSA-2048 private key deterministically from a seed. NOTE: RSA generation in Go is not perfectly deterministic even with a fixed reader. This function is kept for backward compatibility but use of Ed25519 is recommended for true determinism.
func LoadOrGenerateRSAPrivateKey ¶ added in v1.21.0
LoadOrGenerateRSAPrivateKey loads a PEM-encoded private key from disk or generates a new one. It uses standard crypto/rsa (which jwx builds upon) to ensure compatible PEM format.
func Middleware ¶
func Middleware(validator TokenValidator) func(http.Handler) http.Handler
Middleware creates an HTTP middleware that validates JWT tokens. Requests without valid tokens receive 401 Unauthorized.
The middleware extracts the token from the Authorization header:
- "Bearer <token>" format (preferred)
- Raw token (fallback)
Valid claims are stored in the request context and can be retrieved using ClaimsFromContext().
func MiddlewareWithExclusions ¶
func MiddlewareWithExclusions(validator TokenValidator, excludedPaths []string) func(http.Handler) http.Handler
MiddlewareWithExclusions creates a middleware that skips auth for certain paths. This is useful for health checks, public endpoints, etc.
func OptionalMiddleware ¶
func OptionalMiddleware(validator TokenValidator) func(http.Handler) http.Handler
OptionalMiddleware validates tokens if present but doesn't require them. If a valid token is present, claims are added to the context. If no token is present, the request proceeds without claims. If an invalid token is present, the request is rejected.
func RequireRole ¶
RequireRole creates a middleware that requires the user to have a specific role. Must be used after Middleware() in the chain.
Types ¶
type AuthenticatedUser ¶
type AuthenticatedUser struct {
// contains filtered or unexported fields
}
AuthenticatedUser implements a2asrv.User interface. It wraps Hector's Claims to provide user information to a2a-go.
func UserFromCallContext ¶
func UserFromCallContext(callCtx *a2asrv.CallContext) *AuthenticatedUser
UserFromCallContext extracts the AuthenticatedUser from a2a-go CallContext. Returns nil if the user is not authenticated or not an AuthenticatedUser.
func (*AuthenticatedUser) Authenticated ¶
func (u *AuthenticatedUser) Authenticated() bool
Authenticated returns true since this represents an authenticated user.
func (*AuthenticatedUser) Claims ¶
func (u *AuthenticatedUser) Claims() *Claims
Claims returns the underlying Hector claims. This allows access to full claim data when needed.
func (*AuthenticatedUser) Email ¶
func (u *AuthenticatedUser) Email() string
Email returns the user's email address.
func (*AuthenticatedUser) Name ¶
func (u *AuthenticatedUser) Name() string
Name returns the user's subject (unique identifier).
func (*AuthenticatedUser) Role ¶
func (u *AuthenticatedUser) Role() string
Role returns the user's role.
func (*AuthenticatedUser) TenantID ¶
func (u *AuthenticatedUser) TenantID() string
TenantID returns the user's tenant ID.
type Claims ¶
type Claims struct {
// Subject is the unique identifier for the user (sub claim).
Subject string `json:"sub"`
// Email is the user's email address (if provided).
Email string `json:"email,omitempty"`
// Role is the user's role for authorization decisions.
Role string `json:"role,omitempty"`
// TenantID supports multi-tenant applications.
TenantID string `json:"tenant_id,omitempty"`
// Custom contains any additional claims not mapped to struct fields.
Custom map[string]any `json:"-"`
}
Claims represents the validated claims from a JWT token. This structure is designed to support common identity providers (Auth0, Okta, Keycloak, etc.) while being extensible for custom claims.
func ClaimsFromCallContext ¶
func ClaimsFromCallContext(callCtx *a2asrv.CallContext) *Claims
ClaimsFromCallContext extracts Claims from a2a-go CallContext. Returns nil if the user is not authenticated.
func ClaimsFromContext ¶
ClaimsFromContext extracts claims from a context. Returns nil if no claims are present.
func (*Claims) GetStringClaim ¶
GetStringClaim retrieves a custom claim as a string.
func (*Claims) HasAnyRole ¶
HasAnyRole checks if the user has any of the specified roles.
type CompositeValidator ¶ added in v1.15.1
type CompositeValidator struct {
// contains filtered or unexported fields
}
CompositeValidator validates tokens against multiple validators. It returns the claims from the first validator that succeeds.
func NewCompositeValidator ¶ added in v1.15.1
func NewCompositeValidator(validators ...TokenValidator) *CompositeValidator
NewCompositeValidator creates a new CompositeValidator.
func (*CompositeValidator) Close ¶ added in v1.15.1
func (v *CompositeValidator) Close() error
Close closes all validators.
func (*CompositeValidator) ValidateToken ¶ added in v1.15.1
func (v *CompositeValidator) ValidateToken(ctx context.Context, tokenString string) (*Claims, error)
ValidateToken tries each validator in order.
type CredentialType ¶
type CredentialType string
CredentialType identifies the type of credential for outbound requests.
const ( CredentialTypeBearer CredentialType = "bearer" CredentialTypeAPIKey CredentialType = "api_key" CredentialTypeBasic CredentialType = "basic" )
type DeterministicReader ¶ added in v1.21.0
DeterministicReader is a reader that produces a deterministic stream of bytes from a seed.
type Interceptor ¶
type Interceptor struct {
// RequireAuth when true rejects unauthenticated requests.
// When false, unauthenticated requests proceed with a nil User.
RequireAuth bool
}
Interceptor bridges Hector's auth system to a2a-go's CallInterceptor. It reads Claims from the HTTP context (set by Middleware) and sets the a2a-go CallContext.User field.
Architecture ¶
The auth flow is:
- HTTP request arrives
- auth.Middleware validates JWT and stores Claims in context
- a2a-go JSON-RPC handler processes the request
- auth.Interceptor.Before() reads Claims and sets CallContext.User
- Executor/Agent can access User via CallContext
This design keeps JWT validation in standard HTTP middleware while bridging to a2a-go's authentication system.
func NewInterceptor ¶
func NewInterceptor(requireAuth bool) *Interceptor
NewInterceptor creates a new auth interceptor.
func (*Interceptor) After ¶
func (i *Interceptor) After(ctx context.Context, callCtx *a2asrv.CallContext, resp *a2asrv.Response) error
After is called after each a2a-go request handler method. Currently a no-op but can be extended for audit logging.
type JWTValidator ¶
type JWTValidator struct {
// contains filtered or unexported fields
}
JWTValidator validates JWT tokens using JWKS (JSON Web Key Set). It supports automatic key rotation via the jwk.Cache.
This is ported from legacy Hector (pkg/auth/jwt.go) with the same production-tested implementation.
func NewJWTValidator ¶
func NewJWTValidator(cfg JWTValidatorConfig) (*JWTValidator, error)
NewJWTValidator creates a new JWT validator. It fetches the JWKS from the provided URL and caches the keys.
func (*JWTValidator) Close ¶
func (v *JWTValidator) Close() error
Close releases resources held by the validator. Currently a no-op but provided for future resource cleanup.
func (*JWTValidator) ValidateToken ¶
ValidateToken validates a JWT token string and returns the extracted claims. It verifies:
- Token signature against JWKS
- Token expiration (exp claim)
- Token not-before time (nbf claim)
- Issuer (iss claim)
- Audience (aud claim)
type JWTValidatorConfig ¶
type JWTValidatorConfig struct {
// JWKSURL is the URL to fetch JSON Web Key Set from.
// Example: "https://auth.example.com/.well-known/jwks.json"
JWKSURL string
// Issuer is the expected token issuer (iss claim).
// Example: "https://auth.example.com"
Issuer string
// Audience is the expected token audience (aud claim).
// Example: "hector-api"
Audience string
// RefreshInterval is how often to refresh the JWKS.
// Default: 15 minutes
RefreshInterval time.Duration
}
JWTValidatorConfig configures the JWT validator.
type MultiIssuerConfig ¶ added in v1.21.0
type MultiIssuerConfig struct {
// SelfIssuer is the issuer URL for hector-issued tokens.
SelfIssuer string
// SelfValidator validates hector-issued tokens.
SelfValidator TokenValidator
// ExternalValidators maps external issuer URLs to their JWKS validators.
ExternalValidators map[string]TokenValidator
}
MultiIssuerConfig configures the multi-issuer validator.
type MultiIssuerValidator ¶ added in v1.21.0
type MultiIssuerValidator struct {
// contains filtered or unexported fields
}
MultiIssuerValidator validates JWTs from multiple issuers. It extracts the issuer claim from the token and routes to the correct validator. This enables unified auth for both hector-issued tokens and external OIDC tokens.
func NewMultiIssuerValidator ¶ added in v1.21.0
func NewMultiIssuerValidator(cfg MultiIssuerConfig) (*MultiIssuerValidator, error)
NewMultiIssuerValidator creates a new multi-issuer validator.
func (*MultiIssuerValidator) AddExternalValidator ¶ added in v1.21.0
func (v *MultiIssuerValidator) AddExternalValidator(issuer string, validator TokenValidator)
AddExternalValidator adds an external validator for a given issuer.
func (*MultiIssuerValidator) Close ¶ added in v1.21.0
func (v *MultiIssuerValidator) Close() error
Close closes all validators.
func (*MultiIssuerValidator) ValidateToken ¶ added in v1.21.0
func (v *MultiIssuerValidator) ValidateToken(ctx context.Context, tokenString string) (*Claims, error)
ValidateToken validates a JWT token by routing to the correct validator based on issuer.
type SecretValidator ¶ added in v1.15.1
type SecretValidator struct {
// contains filtered or unexported fields
}
SecretValidator validates tokens against a shared secret.
func NewSecretValidator ¶ added in v1.15.1
func NewSecretValidator(secret string) *SecretValidator
NewSecretValidator creates a new SecretValidator.
func (*SecretValidator) Close ¶ added in v1.15.1
func (v *SecretValidator) Close() error
Close is a no-op for SecretValidator.
func (*SecretValidator) ValidateToken ¶ added in v1.15.1
ValidateToken validates the token against the shared secret. It uses constant-time comparison to prevent timing attacks.
type TokenIssuer ¶ added in v1.21.0
type TokenIssuer struct {
// contains filtered or unexported fields
}
TokenIssuer issues JWT tokens for apps. It uses asymmetric keys (RSA or Ed25519) for signing and provides a JWKS endpoint for verification.
func NewTokenIssuer ¶ added in v1.21.0
func NewTokenIssuer(cfg TokenIssuerConfig) (*TokenIssuer, error)
NewTokenIssuer creates a new token issuer. If no private key is provided, it generates a new RSA-2048 key pair.
func (*TokenIssuer) Close ¶ added in v1.21.0
func (i *TokenIssuer) Close() error
Close releases resources held by the issuer.
func (*TokenIssuer) IssueAdminToken ¶ added in v1.21.0
IssueAdminToken creates a JWT token with admin role.
func (*TokenIssuer) IssueToken ¶ added in v1.21.0
IssueToken creates a new JWT token for the given app.
func (*TokenIssuer) Issuer ¶ added in v1.21.0
func (i *TokenIssuer) Issuer() string
Issuer returns the issuer URL.
func (*TokenIssuer) PublicJWKS ¶ added in v1.21.0
func (i *TokenIssuer) PublicJWKS() jwk.Set
PublicJWKS returns the public key set for external verification. This should be served at /.well-known/jwks.json
func (*TokenIssuer) ValidateToken ¶ added in v1.21.0
ValidateToken validates a token issued by this issuer. This is used for in-process validation without HTTP round-trip.
type TokenIssuerConfig ¶ added in v1.21.0
type TokenIssuerConfig struct {
// Issuer is the issuer claim (iss) for tokens.
// Example: "https://hector.example.com"
Issuer string
// Audience is the default audience claim (aud) for tokens.
// Example: "hector-api"
Audience string
// PrivateKeyPEM is optional pre-existing signing key (RSA or Ed25519) in PEM format.
// If not provided, a new RSA key pair will be generated.
PrivateKeyPEM []byte
}
TokenIssuerConfig configures the token issuer.
type TokenProvider ¶
TokenProvider is a function that returns a token for outbound requests.
func NewTokenProvider ¶
func NewTokenProvider(credType CredentialType, token, apiKey, username, password string) (TokenProvider, error)
NewTokenProvider creates a TokenProvider based on credential configuration. This is useful for making authenticated outbound requests (e.g., to remote agents).
type TokenValidator ¶
type TokenValidator interface {
ValidateToken(ctx context.Context, tokenString string) (*Claims, error)
Close() error
}
TokenValidator is the interface for token validation. This allows for alternative implementations (e.g., for testing).
func NewValidatorFromConfig ¶
func NewValidatorFromConfig(cfg *config.AuthConfig) (TokenValidator, error)
NewValidatorFromConfig creates a TokenValidator from configuration. Returns nil if authentication is not enabled.