Documentation
¶
Overview ¶
Package bff implements the Backend for Frontend (BFF) pattern for secure session management. The BFF holds tokens server-side and uses HTTP-only cookies to identify browser sessions.
Index ¶
- Constants
- Variables
- func APIProxyMiddleware(targetURL string) (func(http.Handler) http.Handler, error)
- func AddBFFSecurityScheme(api huma.API, cookieName string)
- func AutoRefreshMiddleware(config RefreshConfig) (func(http.Handler) http.Handler, error)
- func GenerateSessionID() (string, error)
- func GetCloudflareMetadata(r *http.Request) map[string]string
- func OptionalSessionMiddleware(store Store, cookieManager *CookieManager) func(http.Handler) http.Handler
- func OriginMiddleware(allowedOrigins ...string) func(http.Handler) http.Handler
- func RateLimitMiddleware(requestsPerMinute, burstSize int) func(http.Handler) http.Handler
- func RefreshHandler(config RefreshConfig) http.Handler
- func RegisterHumaRoutes(api huma.API, config HumaConfig)
- func RequireSessionMiddleware(store Store, cookieManager *CookieManager) func(http.Handler) http.Handler
- func SessionMiddleware(config MiddlewareConfig) func(http.Handler) http.Handler
- func SimpleProxy(targetURL string) (http.Handler, error)
- type BFFErrorResponse
- type ClientIPConfig
- type ClientIPExtractor
- type CookieConfig
- type CookieManager
- type CreateSessionParams
- type EndpointLimit
- type GetSessionInput
- type GetSessionOutput
- type Handler
- type HandlerConfig
- type HumaConfig
- type LogoutInput
- type LogoutOutput
- type LogoutResponse
- type MemoryStore
- func (s *MemoryStore) Cleanup(ctx context.Context) (int, error)
- func (s *MemoryStore) Close() error
- func (s *MemoryStore) Count() int
- func (s *MemoryStore) Create(ctx context.Context, session *Session) error
- func (s *MemoryStore) Delete(ctx context.Context, id string) error
- func (s *MemoryStore) DeleteByUserID(ctx context.Context, userID string) (int, error)
- func (s *MemoryStore) Get(ctx context.Context, id string) (*Session, error)
- func (s *MemoryStore) Touch(ctx context.Context, id string) error
- func (s *MemoryStore) Update(ctx context.Context, session *Session) error
- type MiddlewareConfig
- type OriginConfig
- type OriginValidator
- type Proxy
- type ProxyConfig
- type RateLimitConfig
- type RateLimitInfo
- type RateLimiter
- type RefreshConfig
- type RefreshInput
- type RefreshOutput
- type RefreshResponse
- type Refresher
- type Session
- func (s *Session) GetDPoPKeyPair() (*dpop.KeyPair, error)
- func (s *Session) HasDPoP() bool
- func (s *Session) IsAccessTokenExpired() bool
- func (s *Session) IsExpired() bool
- func (s *Session) IsRefreshTokenExpired() bool
- func (s *Session) NeedsRefresh(threshold time.Duration) bool
- func (s *Session) SetDPoPKeyPair(kp *dpop.KeyPair) error
- type SessionInfoResponse
- type SessionStatusResponse
- type Store
- type StoreConfig
- type TokenErrorResponse
- type TokenRefreshResult
- type TokenResponse
Constants ¶
const ( // ErrCodeNoSession indicates no session cookie was provided. ErrCodeNoSession = "no_session" // ErrCodeSessionExpired indicates the session has expired. ErrCodeSessionExpired = "session_expired" // ErrCodeInvalidSession indicates the session is invalid. ErrCodeInvalidSession = "invalid_session" // ErrCodeCSRFViolation indicates CSRF protection blocked the request. ErrCodeCSRFViolation = "csrf_violation" // ErrCodeRateLimited indicates rate limit was exceeded. ErrCodeRateLimited = "rate_limited" )
Common error codes for documentation
const (
// ContextKeySession is the context key for the session.
ContextKeySession contextKey = "bff_session"
)
const SessionIDLength = 32
SessionIDLength is the length of generated session IDs in bytes.
Variables ¶
var ( // ErrSessionNotFound is returned when a session is not found. ErrSessionNotFound = errors.New("session not found") // ErrSessionExpired is returned when a session has expired. ErrSessionExpired = errors.New("session expired") // ErrInvalidSession is returned when a session is invalid. ErrInvalidSession = errors.New("invalid session") // ErrStoreRequired is returned when a store is not provided. ErrStoreRequired = errors.New("session store is required") // ErrOriginsRequired is returned when no allowed origins are provided. ErrOriginsRequired = errors.New("allowed origins are required for CSRF protection") )
var ErrNoDPoPKeyPair = errors.New("session has no DPoP key pair")
ErrNoDPoPKeyPair is returned when the session has no DPoP key pair.
var ErrRefreshFailed = errors.New("token refresh failed")
ErrRefreshFailed is returned when token refresh fails.
var ErrRefreshTokenExpired = errors.New("refresh token expired")
ErrRefreshTokenExpired is returned when the refresh token has expired.
var ErrTokenEndpointRequired = errors.New("token endpoint required")
ErrTokenEndpointRequired is returned when the token endpoint is not configured.
Functions ¶
func APIProxyMiddleware ¶
APIProxyMiddleware creates middleware that proxies all requests to an API backend. This is useful for wrapping the proxy handler with session middleware.
func AddBFFSecurityScheme ¶ added in v0.4.0
AddBFFSecurityScheme adds the BFF cookie-based security scheme to the OpenAPI spec.
func AutoRefreshMiddleware ¶
AutoRefreshMiddleware creates middleware that automatically refreshes tokens before expiry.
func GenerateSessionID ¶
GenerateSessionID generates a cryptographically secure session ID.
func GetCloudflareMetadata ¶ added in v0.4.0
GetCloudflareMetadata extracts Cloudflare-specific metadata from the request.
func OptionalSessionMiddleware ¶
func OptionalSessionMiddleware(store Store, cookieManager *CookieManager) func(http.Handler) http.Handler
OptionalSessionMiddleware creates middleware that loads sessions if present. Requests without sessions are allowed through.
func OriginMiddleware ¶
OriginMiddleware creates origin validation middleware with the given allowed origins. This is a convenience function for simple use cases.
func RateLimitMiddleware ¶ added in v0.4.0
RateLimitMiddleware creates rate limiting middleware with default config.
func RefreshHandler ¶
func RefreshHandler(config RefreshConfig) http.Handler
RefreshHandler returns an HTTP handler for explicit token refresh requests. This can be used by the frontend to proactively refresh tokens.
func RegisterHumaRoutes ¶ added in v0.4.0
func RegisterHumaRoutes(api huma.API, config HumaConfig)
RegisterHumaRoutes registers BFF endpoints with a Huma API for OpenAPI generation. The actual request handling is done by chi-based handlers in Handler.Router().
Usage:
// Create BFF handler
bffHandler, _ := bff.NewHandler(config)
// Mount chi routes for actual handling
router.Mount("/bff", bffHandler.Router())
// Register with Huma for OpenAPI spec
bff.RegisterHumaRoutes(humaAPI, bff.HumaConfig{
Handler: bffHandler,
PathPrefix: "/bff",
})
func RequireSessionMiddleware ¶
func RequireSessionMiddleware(store Store, cookieManager *CookieManager) func(http.Handler) http.Handler
RequireSessionMiddleware creates middleware that requires a valid session. This is a convenience function that wraps SessionMiddleware.
func SessionMiddleware ¶
func SessionMiddleware(config MiddlewareConfig) func(http.Handler) http.Handler
SessionMiddleware creates middleware that loads sessions from cookies.
Types ¶
type BFFErrorResponse ¶ added in v0.4.0
type BFFErrorResponse struct {
// Error is the error code.
Error string `json:"error" doc:"Error code" example:"session_expired"`
// Message is the human-readable error message.
Message string `json:"message" doc:"Human-readable error message" example:"Your session has expired"`
}
BFFErrorResponse is returned for BFF-specific errors.
type ClientIPConfig ¶ added in v0.4.0
type ClientIPConfig struct {
// TrustCloudflare enables Cloudflare header support.
// When true, checks CF-Connecting-IP and True-Client-IP headers.
// Default: false.
TrustCloudflare bool
// TrustProxy enables standard proxy headers (X-Forwarded-For, X-Real-IP).
// Only enable if behind a trusted reverse proxy.
// Default: false.
TrustProxy bool
// TrustedProxies is a list of trusted proxy IP ranges.
// If set, proxy headers are only trusted when the request comes from these IPs.
// Supports CIDR notation (e.g., "10.0.0.0/8", "172.16.0.0/12").
TrustedProxies []string
// CloudflareIPRanges can be set to validate that CF headers come from Cloudflare.
// If empty and TrustCloudflare is true, CF headers are trusted unconditionally.
// Cloudflare publishes their IP ranges at:
// https://www.cloudflare.com/ips-v4 and https://www.cloudflare.com/ips-v6
CloudflareIPRanges []string
// contains filtered or unexported fields
}
ClientIPConfig contains configuration for client IP extraction.
func CloudflareClientIPConfig ¶ added in v0.4.0
func CloudflareClientIPConfig() ClientIPConfig
CloudflareClientIPConfig returns configuration for Cloudflare deployments.
func DefaultClientIPConfig ¶ added in v0.4.0
func DefaultClientIPConfig() ClientIPConfig
DefaultClientIPConfig returns a safe default configuration. By default, no proxy headers are trusted.
type ClientIPExtractor ¶ added in v0.4.0
type ClientIPExtractor struct {
// contains filtered or unexported fields
}
ClientIPExtractor extracts the real client IP from requests.
func NewClientIPExtractor ¶ added in v0.4.0
func NewClientIPExtractor(config ClientIPConfig) *ClientIPExtractor
NewClientIPExtractor creates a new client IP extractor.
func (*ClientIPExtractor) GetClientIP ¶ added in v0.4.0
func (e *ClientIPExtractor) GetClientIP(r *http.Request) string
GetClientIP extracts the real client IP from the request. It checks headers in order of trust: Cloudflare > Proxy > RemoteAddr.
type CookieConfig ¶
type CookieConfig struct {
// Name is the cookie name. Default: "cf_session".
Name string
// Domain is the cookie domain. If empty, uses the request host.
Domain string
// Path is the cookie path. Default: "/".
Path string
// MaxAge is the cookie max age in seconds. Default: 0 (session cookie).
// Set to -1 to delete the cookie.
MaxAge int
// Secure indicates the cookie should only be sent over HTTPS.
// Default: true.
Secure bool
// HTTPOnly prevents JavaScript access to the cookie.
// Default: true (required for security).
HTTPOnly bool
// SameSite controls cross-site cookie behavior.
// Default: SameSiteStrictMode.
SameSite http.SameSite
}
CookieConfig contains configuration for session cookies.
func DefaultCookieConfig ¶
func DefaultCookieConfig() CookieConfig
DefaultCookieConfig returns secure default cookie configuration.
type CookieManager ¶
type CookieManager struct {
// contains filtered or unexported fields
}
CookieManager handles session cookie operations.
func NewCookieManager ¶
func NewCookieManager(config CookieConfig) *CookieManager
NewCookieManager creates a new cookie manager with the given configuration.
func (*CookieManager) ClearSessionCookie ¶
func (m *CookieManager) ClearSessionCookie(w http.ResponseWriter)
ClearSessionCookie removes the session cookie from the response.
func (*CookieManager) Config ¶
func (m *CookieManager) Config() CookieConfig
Config returns the cookie configuration.
func (*CookieManager) GetSessionID ¶
func (m *CookieManager) GetSessionID(r *http.Request) string
GetSessionID extracts the session ID from the request cookie. Returns empty string if the cookie is not present.
func (*CookieManager) SetSessionCookie ¶
func (m *CookieManager) SetSessionCookie(w http.ResponseWriter, sessionID string, expiry time.Time)
SetSessionCookie creates and sets a session cookie on the response.
type CreateSessionParams ¶ added in v0.4.0
type CreateSessionParams struct {
UserID uuid.UUID
OrganizationID *uuid.UUID
AccessToken string
RefreshToken string
AccessTokenExpiresIn time.Duration
RefreshTokenExpiresIn time.Duration
DPoPKeyPairJSON []byte // Serialized DPoP key pair (from dpop.KeyPair.SerializeJSON())
DPoPThumbprint string // JWK thumbprint of the DPoP key pair
Metadata map[string]string
}
CreateSessionParams contains parameters for creating a session.
type EndpointLimit ¶ added in v0.4.0
EndpointLimit defines rate limits for a specific endpoint.
type GetSessionInput ¶ added in v0.4.0
type GetSessionInput struct{}
GetSessionInput is the input for getting session status.
type GetSessionOutput ¶ added in v0.4.0
type GetSessionOutput struct {
Body SessionStatusResponse
}
GetSessionOutput is the response for session status.
type Handler ¶ added in v0.4.0
type Handler struct {
// contains filtered or unexported fields
}
Handler provides BFF endpoints with built-in security.
func NewHandler ¶ added in v0.4.0
func NewHandler(config HandlerConfig) (*Handler, error)
NewHandler creates a new BFF handler.
func (*Handler) CookieManager ¶ added in v0.4.0
func (h *Handler) CookieManager() *CookieManager
CookieManager returns the cookie manager.
func (*Handler) CreateSession ¶ added in v0.4.0
func (h *Handler) CreateSession(ctx context.Context, w http.ResponseWriter, r *http.Request, params CreateSessionParams) (*Session, error)
CreateSession creates a new BFF session and sets the cookie. This is typically called after OAuth callback completes.
type HandlerConfig ¶ added in v0.4.0
type HandlerConfig struct {
// Store is the session store. Required.
Store Store
// CookieConfig configures session cookies.
// If zero value, uses DefaultCookieConfig().
CookieConfig CookieConfig
// ProxyConfig configures the API proxy.
// TargetURL is required if using the proxy.
ProxyConfig ProxyConfig
// AllowedOrigins is a list of allowed origins for CSRF protection.
// Required for security. Example: ["https://myapp.com", "https://app.myapp.com"]
AllowedOrigins []string
// ClientIPConfig configures client IP extraction.
// If zero value, uses DefaultClientIPConfig().
ClientIPConfig ClientIPConfig
// SessionIDGenerator generates session IDs.
// If nil, uses GenerateSessionID().
SessionIDGenerator func() (string, error)
// OnCreateSession is called when a session is created.
// Can be used to persist refresh tokens to a database.
OnCreateSession func(ctx context.Context, session *Session) error
// OnRefresh is called to refresh tokens.
// Must return new access token, optional new refresh token, and expiry.
// If nil, the /refresh endpoint returns 501 Not Implemented.
OnRefresh func(ctx context.Context, session *Session) (*TokenRefreshResult, error)
// OnLogout is called when a session is logged out.
// Can be used to revoke refresh tokens in the database.
OnLogout func(ctx context.Context, session *Session) error
// OnSessionLoad is called after a session is loaded from the store.
// Can be used for logging or enriching session data.
OnSessionLoad func(ctx context.Context, session *Session) error
// BasePath is the base path for BFF routes.
// Default: "" (routes at root of the mounted router).
BasePath string
// APIPathPrefix is the path prefix for proxied API requests.
// Default: "/api".
APIPathPrefix string
// EnableProxyForPublicRoutes allows unauthenticated proxy requests.
// When true, /api/* routes work without a session (for public APIs).
// Default: false.
EnableProxyForPublicRoutes bool
// RateLimitConfig configures rate limiting for BFF endpoints.
// If nil, rate limiting is disabled.
RateLimitConfig *RateLimitConfig
}
HandlerConfig contains configuration for the BFF handler.
type HumaConfig ¶ added in v0.4.0
type HumaConfig struct {
// Handler is the BFF handler to use for operations.
Handler *Handler
// PathPrefix is the prefix for all BFF routes.
// Default: "/bff".
PathPrefix string
// Tags are OpenAPI tags for BFF endpoints.
// Default: ["BFF"].
Tags []string
// IncludeRateLimitDocs adds x-ratelimit-* extensions to OpenAPI.
// Default: true.
IncludeRateLimitDocs bool
}
HumaConfig contains configuration for Huma BFF registration.
type LogoutInput ¶ added in v0.4.0
type LogoutInput struct {
Origin string `header:"Origin" required:"true" doc:"Origin header for CSRF protection"`
}
LogoutInput is the input for logout.
type LogoutOutput ¶ added in v0.4.0
type LogoutOutput struct {
Body LogoutResponse
}
LogoutOutput is the response for logout.
type LogoutResponse ¶ added in v0.4.0
type LogoutResponse struct {
// Message indicates the logout result.
Message string `json:"message" doc:"Logout result message" example:"Logged out successfully"`
}
LogoutResponse contains the logout result.
type MemoryStore ¶
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore is an in-memory session store for development and testing. It is thread-safe and supports automatic cleanup of expired sessions.
func NewMemoryStore ¶
func NewMemoryStore(config StoreConfig) *MemoryStore
NewMemoryStore creates a new in-memory session store.
func (*MemoryStore) Cleanup ¶
func (s *MemoryStore) Cleanup(ctx context.Context) (int, error)
Cleanup removes expired sessions.
func (*MemoryStore) Close ¶
func (s *MemoryStore) Close() error
Close stops the cleanup goroutine and releases resources.
func (*MemoryStore) Count ¶
func (s *MemoryStore) Count() int
Count returns the current number of sessions (for testing).
func (*MemoryStore) Create ¶
func (s *MemoryStore) Create(ctx context.Context, session *Session) error
Create stores a new session.
func (*MemoryStore) Delete ¶
func (s *MemoryStore) Delete(ctx context.Context, id string) error
Delete removes a session by ID.
func (*MemoryStore) DeleteByUserID ¶
DeleteByUserID removes all sessions for a user.
type MiddlewareConfig ¶
type MiddlewareConfig struct {
// Store is the session store.
Store Store
// CookieManager handles session cookies.
CookieManager *CookieManager
// RefreshThreshold is how long before access token expiry to trigger refresh.
// Default: 5 minutes.
RefreshThreshold time.Duration
// OnSessionLoad is called after a session is loaded.
// Can be used for logging or session modification.
OnSessionLoad func(ctx context.Context, session *Session) error
// OnSessionExpired is called when a session is expired.
OnSessionExpired func(w http.ResponseWriter, r *http.Request)
// OnSessionInvalid is called when a session is invalid.
OnSessionInvalid func(w http.ResponseWriter, r *http.Request)
// OnNoSession is called when no session is found and RequireSession is true.
OnNoSession func(w http.ResponseWriter, r *http.Request)
// RequireSession when true rejects requests without valid sessions.
RequireSession bool
// TouchOnAccess when true updates LastAccessedAt on each request.
TouchOnAccess bool
}
MiddlewareConfig contains configuration for the BFF middleware.
type OriginConfig ¶
type OriginConfig struct {
// AllowedOrigins is a list of allowed origins.
// Origins should be in the format "https://example.com" (no trailing slash).
AllowedOrigins []string
// AllowedHosts is a list of allowed hosts (without scheme).
// This is an alternative to AllowedOrigins for simpler configuration.
AllowedHosts []string
// OnError is called when origin validation fails.
// If nil, returns 403 Forbidden.
OnError func(w http.ResponseWriter, r *http.Request)
// AllowMissingOrigin allows requests without Origin header.
// Default: false (more secure).
AllowMissingOrigin bool
// CheckReferer uses Referer header as fallback when Origin is missing.
// Default: true.
CheckReferer bool
// SkipMethods is a list of HTTP methods to skip origin validation.
// Typically safe methods (GET, HEAD, OPTIONS) can be skipped.
// Default: none (validate all methods).
SkipMethods []string
}
OriginConfig contains configuration for origin validation.
func DefaultOriginConfig ¶
func DefaultOriginConfig() OriginConfig
DefaultOriginConfig returns default origin validation configuration.
type OriginValidator ¶
type OriginValidator struct {
// contains filtered or unexported fields
}
OriginValidator validates request origins.
func NewOriginValidator ¶
func NewOriginValidator(config OriginConfig) *OriginValidator
NewOriginValidator creates a new origin validator.
func (*OriginValidator) Middleware ¶
func (v *OriginValidator) Middleware() func(http.Handler) http.Handler
Middleware returns HTTP middleware that validates request origins.
func (*OriginValidator) ValidateRequest ¶
func (v *OriginValidator) ValidateRequest(r *http.Request) bool
ValidateRequest validates the request origin.
type Proxy ¶
type Proxy struct {
// contains filtered or unexported fields
}
Proxy proxies requests to an API backend with session-based authentication.
type ProxyConfig ¶
type ProxyConfig struct {
// TargetURL is the base URL of the API backend.
TargetURL string
// UseDPoP enables DPoP proof injection.
// Default: true
UseDPoP bool
// Client is the HTTP client to use for proxied requests.
// If nil, uses http.DefaultClient.
Client *http.Client
// Timeout is the request timeout.
// Default: 30 seconds.
Timeout time.Duration
// OnError is called when a proxy error occurs.
// If nil, returns 502 Bad Gateway.
OnError func(w http.ResponseWriter, r *http.Request, err error)
// OnRequestRewrite allows modifying the proxied request before sending.
OnRequestRewrite func(r *http.Request, session *Session)
// PathRewrite allows modifying the request path.
// The function receives the original path and returns the rewritten path.
PathRewrite func(path string) string
// StripPrefix removes a prefix from the request path before proxying.
StripPrefix string
// HeadersToForward specifies which request headers to forward.
// If empty, forwards all headers except hop-by-hop headers.
HeadersToForward []string
// HeadersToRemove specifies headers to remove from the proxied request.
HeadersToRemove []string
// ResponseHeadersToRemove specifies headers to remove from the response.
ResponseHeadersToRemove []string
}
ProxyConfig contains configuration for the API proxy.
func DefaultProxyConfig ¶
func DefaultProxyConfig() ProxyConfig
DefaultProxyConfig returns default proxy configuration.
type RateLimitConfig ¶ added in v0.4.0
type RateLimitConfig struct {
// RequestsPerMinute is the sustained request rate.
// Default: 60.
RequestsPerMinute int
// BurstSize is the maximum burst of requests allowed.
// Default: 10.
BurstSize int
// EndpointLimits allows per-endpoint rate limit overrides.
// Key is the endpoint path (e.g., "/refresh", "/logout").
EndpointLimits map[string]EndpointLimit
// KeyFunc extracts the rate limit key from a request.
// Default: client IP address.
KeyFunc func(r *http.Request) string
// OnLimitExceeded is called when rate limit is exceeded.
// If nil, returns 429 Too Many Requests with standard headers.
OnLimitExceeded func(w http.ResponseWriter, r *http.Request, info RateLimitInfo)
// ExcludePaths are paths that bypass rate limiting.
// Example: []string{"/health", "/metrics"}
ExcludePaths []string
// TrustCloudflare uses CF-Connecting-IP for client identification.
// Default: false.
TrustCloudflare bool
// CleanupInterval is how often to clean up expired entries.
// Default: 1 minute.
CleanupInterval time.Duration
}
RateLimitConfig contains configuration for rate limiting.
func DefaultRateLimitConfig ¶ added in v0.4.0
func DefaultRateLimitConfig() RateLimitConfig
DefaultRateLimitConfig returns sensible default rate limit configuration.
type RateLimitInfo ¶ added in v0.4.0
type RateLimitInfo struct {
Limit int // Maximum requests per window
Remaining int // Requests remaining in current window
ResetAt time.Time // When the window resets
RetryAfter int // Seconds until next request allowed (if limited)
}
RateLimitInfo contains information about the current rate limit state.
type RateLimiter ¶ added in v0.4.0
type RateLimiter struct {
// contains filtered or unexported fields
}
RateLimiter implements token bucket rate limiting.
func NewRateLimiter ¶ added in v0.4.0
func NewRateLimiter(config RateLimitConfig) *RateLimiter
NewRateLimiter creates a new rate limiter.
func (*RateLimiter) Close ¶ added in v0.4.0
func (rl *RateLimiter) Close()
Close stops the cleanup goroutine.
func (*RateLimiter) Middleware ¶ added in v0.4.0
func (rl *RateLimiter) Middleware() func(http.Handler) http.Handler
Middleware returns HTTP middleware that enforces rate limits.
type RefreshConfig ¶
type RefreshConfig struct {
// Store is the session store.
Store Store
// CookieManager handles session cookies.
CookieManager *CookieManager
// TokenEndpoint is the OAuth token endpoint URL.
TokenEndpoint string
// ClientID is the OAuth client ID.
ClientID string
// ClientSecret is the OAuth client secret (optional for public clients).
ClientSecret string //nolint:gosec // G117: config field, not a hardcoded secret
// UseDPoP enables DPoP for token refresh requests.
// When true, generates a new DPoP key pair and binds the new tokens to it.
// Default: true.
UseDPoP bool
// Client is the HTTP client to use for refresh requests.
// If nil, uses http.DefaultClient.
Client *http.Client
// Timeout is the refresh request timeout.
// Default: 30 seconds.
Timeout time.Duration
// RefreshThreshold is how early before expiry to refresh tokens.
// Default: 5 minutes.
RefreshThreshold time.Duration
// OnRefreshSuccess is called when tokens are successfully refreshed.
OnRefreshSuccess func(ctx context.Context, session *Session)
// OnRefreshError is called when token refresh fails.
OnRefreshError func(w http.ResponseWriter, r *http.Request, err error)
// ParseTokenResponse allows custom parsing of the token response.
// If nil, uses the default OAuth2 token response format.
ParseTokenResponse func(body []byte) (*TokenResponse, error)
}
RefreshConfig contains configuration for the token refresh handler.
func DefaultRefreshConfig ¶
func DefaultRefreshConfig() RefreshConfig
DefaultRefreshConfig returns default refresh configuration.
type RefreshInput ¶ added in v0.4.0
type RefreshInput struct {
Origin string `header:"Origin" required:"true" doc:"Origin header for CSRF protection"`
}
RefreshInput is the input for token refresh.
type RefreshOutput ¶ added in v0.4.0
type RefreshOutput struct {
Body RefreshResponse
// Standard rate limit headers
RateLimitLimit int `header:"X-RateLimit-Limit" doc:"Maximum requests per window"`
RateLimitRemaining int `header:"X-RateLimit-Remaining" doc:"Requests remaining in current window"`
RateLimitReset int64 `header:"X-RateLimit-Reset" doc:"Unix timestamp when window resets"`
}
RefreshOutput is the response for token refresh.
type RefreshResponse ¶ added in v0.4.0
type RefreshResponse struct {
// Message indicates the refresh result.
Message string `json:"message" doc:"Refresh result message" example:"Session refreshed"`
// ExpiresAt is the Unix timestamp when the new access token expires.
ExpiresAt int64 `json:"expires_at" doc:"Unix timestamp when access token expires"`
}
RefreshResponse contains the refresh result.
type Refresher ¶
type Refresher struct {
// contains filtered or unexported fields
}
Refresher handles automatic token refresh.
func NewRefresher ¶
func NewRefresher(config RefreshConfig) (*Refresher, error)
NewRefresher creates a new token refresher.
func (*Refresher) Middleware ¶
Middleware returns HTTP middleware that automatically refreshes expired access tokens.
type Session ¶
type Session struct {
// ID is the unique session identifier.
ID string `json:"id"`
// UserID is the authenticated user's ID.
UserID uuid.UUID `json:"user_id"`
// OrganizationID is the current organization context (optional).
OrganizationID *uuid.UUID `json:"organization_id,omitempty"`
// AccessToken is the OAuth access token.
AccessToken string `json:"access_token"`
// RefreshToken is the OAuth refresh token.
RefreshToken string `json:"refresh_token"`
// AccessTokenExpiresAt is when the access token expires.
AccessTokenExpiresAt time.Time `json:"access_token_expires_at"`
// RefreshTokenExpiresAt is when the refresh token expires.
RefreshTokenExpiresAt time.Time `json:"refresh_token_expires_at"`
// DPoPKeyPairJSON is the serialized DPoP key pair for this session.
// The BFF uses this to sign DPoP proofs when proxying requests to the API.
DPoPKeyPairJSON []byte `json:"dpop_key_pair,omitempty"`
// DPoPThumbprint is the JWK thumbprint of the DPoP key pair.
DPoPThumbprint string `json:"dpop_thumbprint,omitempty"`
// Metadata contains optional session metadata.
Metadata map[string]string `json:"metadata,omitempty"`
// CreatedAt is when the session was created.
CreatedAt time.Time `json:"created_at"`
// UpdatedAt is when the session was last updated.
UpdatedAt time.Time `json:"updated_at"`
// LastAccessedAt is when the session was last accessed.
LastAccessedAt time.Time `json:"last_accessed_at"`
// ExpiresAt is when the session expires (based on refresh token or absolute timeout).
ExpiresAt time.Time `json:"expires_at"`
// IPAddress is the IP address that created the session.
IPAddress string `json:"ip_address,omitempty"`
// UserAgent is the user agent that created the session.
UserAgent string `json:"user_agent,omitempty"`
}
Session represents a server-side session with stored tokens and DPoP keys.
func GetSession ¶
GetSession retrieves the session from the request context.
func NewSession ¶
func NewSession(userID uuid.UUID, accessToken, refreshToken string, accessExpiry, refreshExpiry time.Duration) (*Session, error)
NewSession creates a new session with the given parameters.
func (*Session) GetDPoPKeyPair ¶
GetDPoPKeyPair deserializes and returns the session's DPoP key pair.
func (*Session) IsAccessTokenExpired ¶
IsAccessTokenExpired returns true if the access token has expired.
func (*Session) IsRefreshTokenExpired ¶
IsRefreshTokenExpired returns true if the refresh token has expired.
func (*Session) NeedsRefresh ¶
NeedsRefresh returns true if the access token needs to be refreshed. This returns true if the access token is expired or will expire soon.
type SessionInfoResponse ¶ added in v0.4.0
type SessionInfoResponse struct {
Authenticated bool `json:"authenticated"`
UserID *uuid.UUID `json:"user_id,omitempty"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
}
SessionInfoResponse is the response for session info requests.
type SessionStatusResponse ¶ added in v0.4.0
type SessionStatusResponse struct {
// Authenticated indicates if the user has a valid session.
Authenticated bool `json:"authenticated" doc:"Whether the user has a valid session"`
// UserID is the authenticated user's ID (only present if authenticated).
UserID *uuid.UUID `json:"user_id,omitempty" doc:"The authenticated user's ID"`
// OrganizationID is the current organization context (only present if set).
OrganizationID *uuid.UUID `json:"organization_id,omitempty" doc:"Current organization context"`
// ExpiresAt is when the session expires (only present if authenticated).
ExpiresAt *time.Time `json:"expires_at,omitempty" doc:"When the session expires" format:"date-time"`
// AccessTokenExpiresAt is when the access token expires (only present if authenticated).
AccessTokenExpiresAt *time.Time `json:"access_token_expires_at,omitempty" doc:"When the access token expires" format:"date-time"`
}
SessionStatusResponse contains session status information.
type Store ¶
type Store interface {
// Create stores a new session and returns the session ID.
Create(ctx context.Context, session *Session) error
// Get retrieves a session by ID.
// Returns ErrSessionNotFound if the session doesn't exist.
// Returns ErrSessionExpired if the session has expired.
Get(ctx context.Context, id string) (*Session, error)
// Update updates an existing session.
// Returns ErrSessionNotFound if the session doesn't exist.
Update(ctx context.Context, session *Session) error
// Delete removes a session by ID.
// Returns ErrSessionNotFound if the session doesn't exist.
Delete(ctx context.Context, id string) error
// DeleteByUserID removes all sessions for a user.
// Returns the number of sessions deleted.
DeleteByUserID(ctx context.Context, userID string) (int, error)
// Touch updates the LastAccessedAt timestamp.
// This is used to track session activity without modifying other fields.
Touch(ctx context.Context, id string) error
// Cleanup removes expired sessions.
// Returns the number of sessions removed.
Cleanup(ctx context.Context) (int, error)
// Close closes the store and releases any resources.
Close() error
}
Store defines the interface for session storage. Implementations must be safe for concurrent use.
type StoreConfig ¶
type StoreConfig struct {
// CleanupInterval is how often to run automatic cleanup.
// Set to 0 to disable automatic cleanup.
CleanupInterval int
// MaxSessions is the maximum number of sessions to store (0 = unlimited).
MaxSessions int
// EncryptionKey is used to encrypt sensitive session data.
// If nil, session data is stored unencrypted.
EncryptionKey []byte
}
StoreConfig contains common configuration for session stores.
func DefaultStoreConfig ¶
func DefaultStoreConfig() StoreConfig
DefaultStoreConfig returns sensible default configuration.
type TokenErrorResponse ¶
type TokenErrorResponse struct {
Error string `json:"error"`
ErrorDescription string `json:"error_description,omitempty"`
}
TokenErrorResponse represents an OAuth2 error response.
type TokenRefreshResult ¶ added in v0.4.0
type TokenRefreshResult struct {
AccessToken string
RefreshToken string // Optional: new refresh token
AccessTokenExpiresIn time.Duration
RefreshTokenExpiresIn time.Duration // Optional: if new refresh token issued
}
TokenRefreshResult contains the result of a token refresh operation.
type TokenResponse ¶
type TokenResponse struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token,omitempty"`
Scope string `json:"scope,omitempty"`
}
TokenResponse represents the OAuth2 token response.