Documentation
¶
Index ¶
- func Middleware(userProfileService UserProfileService, tokenExtractor *TokenExtractor) func(http.Handler) http.Handler
- func RequireAuth(userProfileService UserProfileService, tokenExtractor *TokenExtractor) func(http.Handler) http.Handler
- type AllowedGrantType
- type AuthContext
- type ClientContext
- type ClientPrincipal
- type Config
- type ContextKey
- type Manager
- type Principal
- type PrincipalType
- type TokenExtractor
- type UserContext
- type UserPrincipal
- type UserProfileService
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Middleware ¶
func Middleware(userProfileService UserProfileService, tokenExtractor *TokenExtractor) func(http.Handler) http.Handler
Middleware creates an HTTP middleware that extracts and injects authentication context. This middleware: 1. Extracts the Authorization header 2. Parses the token into a user principal or client principal 3. For user principals, resolves user profile idempotently if UserProfileService is provided 4. Injects the auth context into the request
Behavior summary: - Missing Authorization header: request proceeds without auth context. - Invalid token: request is rejected with 401. - Auth dependencies unavailable: request is rejected with 500. - User principal on first login: resolves (get-or-create) user profile if service is provided.
This design allows: - Public endpoints (no auth required) - Protected endpoints (check for context) - Optional auth endpoints (use context if available) - Generic auth that works with or without a user profile service
func RequireAuth ¶
func RequireAuth(userProfileService UserProfileService, tokenExtractor *TokenExtractor) func(http.Handler) http.Handler
RequireAuth returns a middleware that requires authentication. If no auth context is found, returns 401 Unauthorized. This middleware should be applied to protected endpoints.
Usage:
mux.Handle("POST /api/protected", auth.RequireAuth(userProfileService, tokenExtractor)(handler))
TODO_JWT_FUTURE: Consider adding: - Different auth levels (basic, standard, admin) - Claim validation beyond token signature - Rate limiting per user
Types ¶
type AllowedGrantType ¶
type AllowedGrantType string
const ( AuthorizationCodeGrant AllowedGrantType = "authorization_code" ClientCredentialsGrant AllowedGrantType = "client_credentials" )
type AuthContext ¶
type AuthContext struct {
User *UserContext
Client *ClientContext
}
AuthContext is the transient authentication context injected into each request by the auth middleware. For user principals, User contains identity fields and roles. For client principals (M2M), Client is set.
func GetAuthContext ¶
func GetAuthContext(ctx context.Context) *AuthContext
GetAuthContext extracts the AuthContext from a request context. Returns nil if no auth context is available (for example: public route, missing auth header, or middleware not applied).
Usage in handlers:
authCtx := auth.GetAuthContext(r.Context())
if authCtx == nil {
// Handle unauthorized request
}
userID := authCtx.User.ID
func (*AuthContext) Roles ¶
func (a *AuthContext) Roles() []string
Roles returns the granted roles for the principal (user or client), or nil.
func (*AuthContext) Scopes ¶
func (a *AuthContext) Scopes() []string
Scopes returns the granted OAuth2 scopes for the principal (user or client), or nil.
func (*AuthContext) Subject ¶
func (a *AuthContext) Subject() string
Subject returns a stable identifier for the principal: the resolved user ID (falling back to the IdP user ID) for users, the client ID for clients, or "".
func (*AuthContext) Type ¶
func (a *AuthContext) Type() PrincipalType
Type reports the principal type of the context: UserPrincipalType, ClientPrincipalType, or "" when unauthenticated.
type ClientContext ¶
type ClientContext struct {
ClientID string `json:"clientId"`
Roles []string `json:"roles"`
Scopes []string `json:"scopes"`
}
ClientContext represents a machine client's context.
type ClientPrincipal ¶
type Config ¶
type ContextKey ¶
type ContextKey string
ContextKey is a custom type for context keys to avoid collisions.
const AuthContextKey ContextKey = "authContext"
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager handles all authentication-related operations and middleware setup. It encapsulates the token extraction and middleware creation, providing a clean interface for the HTTP server to use.
This manager pattern keeps auth logic self-contained and optionally delegates user persistence to a UserProfileService (if provided). The manager can work without a user profile service, making it suitable for any authentication use case.
func NewManager ¶
func NewManager(userProfileService UserProfileService, authConfig Config) (*Manager, error)
NewManager creates and initializes a new auth manager. This is the single entry point for all auth initialization in the application.
userProfileService is OPTIONAL. If not provided (nil), user creation on first login is disabled. tokenExtractor is REQUIRED and is always initialized by this constructor. This allows the auth package to be used in systems that don't track user profiles while still guaranteeing auth token parsing is available.
Usage examples:
// With user profile service (NSW example)
userProfileService := user.NewService(db)
authManager := auth.NewManager(userProfileService, cfg.Auth)
// Without user profile service (generic auth only)
authManager := auth.NewManager(nil, cfg.Auth)
// With custom user profile service
customService := &MyCustomUserService{}
authManager := auth.NewManager(customService, cfg.Auth)
func (*Manager) Close ¶
Close performs any cleanup needed for the auth manager. Currently a no-op, but provided for future extensibility (e.g., closing JWT validators, flushing caches, logging stats).
Usage:
authManager := auth.NewManager(userService, cfg.Auth) defer authManager.Close()
func (*Manager) Health ¶
Health checks if the auth system is functioning properly. Since the UserProfileService is optional, this only verifies that the auth system components are initialized correctly.
Usage in server startup:
authManager := auth.NewManager(userProfileService, cfg.Auth)
if err := authManager.Health(); err != nil {
log.Fatalf("auth system health check failed: %v", err)
}
Returns an error if auth system components are misconfigured.
func (*Manager) Middleware ¶
Middleware returns the auth middleware function. This middleware extracts and injects authentication context into requests.
Usage:
handler := authManager.Middleware()(mux)
The middleware: 1. Extracts Authorization header 2. Parses token into user or client principal 3. For user principals, creates user record if it's their first login 4. Injects context into request
func (*Manager) OptionalAuthMiddleware ¶
OptionalAuthMiddleware returns a middleware for endpoints that work with or without auth. This is the same as the basic Middleware() - included for semantic clarity.
Usage:
mux.Handle("GET /api/products",
authManager.OptionalAuthMiddleware()(handler),
)
The handler can check if auth context is available and personalize response.
func (*Manager) RequireAuthMiddleware ¶
RequireAuthMiddleware returns a middleware that requires authentication. If no auth context is found, returns 401 Unauthorized. Use this for protected endpoints.
Usage:
mux.Handle("POST /api/protected",
authManager.RequireAuthMiddleware()(handler),
)
type Principal ¶
type Principal struct {
Type PrincipalType `json:"type"`
UserPrincipal *UserPrincipal `json:"userPrincipal,omitempty"`
ClientPrincipal *ClientPrincipal `json:"clientPrincipal,omitempty"`
}
type PrincipalType ¶
type PrincipalType string
const ( UserPrincipalType PrincipalType = "user" ClientPrincipalType PrincipalType = "client" )
type TokenExtractor ¶
type TokenExtractor struct {
// contains filtered or unexported fields
}
TokenExtractor handles token extraction and parsing from HTTP headers. It validates JWT signatures using JWKS and resolves a user principal or client principal based on grant type.
func NewTokenExtractor ¶
func NewTokenExtractor(jwksURL, issuer, audience string, expectedClientIDs []string) (*TokenExtractor, error)
func (*TokenExtractor) ExtractPrincipalFromHeader ¶
func (te *TokenExtractor) ExtractPrincipalFromHeader(authHeader string) (*Principal, error)
ExtractPrincipalFromHeader extracts the principal from Authorization header. Expected header format: "Bearer <jwt_token>". JWT signature is validated against configured JWKS endpoint, then claims are mapped into either UserPrincipal or ClientPrincipal.
type UserContext ¶
type UserContext struct {
ID string `json:"id"`
IDPUserID string `json:"idpUserId"`
Email string `json:"email"`
PhoneNumber string `json:"phoneNumber"`
OUID string `json:"ouId"`
OUHandle string `json:"ouHandle"`
Roles []string `json:"roles"`
Scopes []string `json:"scopes"`
}
UserContext represents a user principal's runtime context injected into each request. It includes identity fields and principal-derived roles. Note: Per-request NSWData is not persisted here; services requiring user metadata should call the user profile service on-demand.
type UserPrincipal ¶
type UserProfileService ¶
type UserProfileService interface {
// GetOrCreateUser creates or retrieves a user profile.
// Parameters:
// - idpUserID: the unique user ID from the identity provider (required)
// - email: user's email address (required)
// - phone: user's phone number (can be empty)
// - organizationID: organization/tenant identifier (required)
// - ouHandle: organization unit handle from the identity provider (required)
//
// Implementation notes:
// - Should be idempotent: calling multiple times with same idpUserID should be safe
// - Called during first login after token validation
// - Errors are logged but don't block authentication
// - Should not return error if user already exists
// Returns user ID of the created or existing user, or an error if the operation fails.
GetOrCreateUser(ctx context.Context, idpUserID, email, phone, orgID, ouHandle string) (string, error)
}
UserProfileService defines the contract for managing user profiles. Implementations are responsible for persisting and managing user records in their system.
This interface is OPTIONAL when using the auth package. If not provided (nil), user creation on first login will be skipped. This allows:
1. Systems that don't track user profiles - just use auth for token validation 2. Systems that manage user profiles separately - implement this interface 3. Systems that handle user creation elsewhere - pass nil
Example implementation:
type MyUserService struct {
db *sql.DB
}
func (s *MyUserService) GetOrCreateUser(ctx context.Context, idpUserID, email, phone, orgID, ouHandle string) (string, error) {
// Your implementation to create or fetch the user idempotently
persistedID := "generated-id"
if err := s.db.Exec("INSERT INTO users ...", idpUserID, email, phone, orgID).Error; err != nil {
return "", err
}
return persistedID, nil
}
authManager := auth.NewManager(myUserService, cfg.Auth) // myUserService can be nil