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 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 CredentialType
- type Interceptor
- type JWTValidator
- type JWTValidatorConfig
- 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 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 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 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 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.