auth

package
v1.6.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 28, 2026 License: Apache-2.0 Imports: 29 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// ClaimsContextKey stores JWT claims in the request context
	ClaimsContextKey = contextKey("claims")

	// ErrorContextKey stores verification errors in the request context
	ErrorContextKey = contextKey("error")
)
View Source
const (
	// ClaimSubject is the subject claim (user ID or "root")
	ClaimSubject = "sub"

	// ClaimRole is the role claim ("admin" or "readonly")
	ClaimRole = "role"

	// ClaimType is the token type claim ("session", "service-account", or "root")
	ClaimType = "type"

	// ClaimIssuedAt is the issued at timestamp claim
	ClaimIssuedAt = "iat"

	// ClaimExpiresAt is the expiration timestamp claim (omitted for root tokens)
	ClaimExpiresAt = "exp"

	// ClaimJTI is the JWT ID claim for tracking/revocation
	ClaimJTI = "jti"
)

Standard JWT claim names used in Neuwerk tokens

View Source
const (
	// RoleAdmin grants full API access (all HTTP methods)
	RoleAdmin = "admin"

	// RoleReadonly grants read-only access (GET only)
	RoleReadonly = "readonly"
)

Role constants define access levels

View Source
const (
	// TokenTypeSession represents browser session tokens
	TokenTypeSession = "session"

	// TokenTypeServiceAccount represents long-lived API tokens
	TokenTypeServiceAccount = "service-account"

	// TokenTypeRoot represents bootstrap-derived admin tokens
	TokenTypeRoot = "root"
)

TokenType constants define token purposes

Variables

This section is empty.

Functions

func DeriveRootToken

func DeriveRootToken(bootstrapToken token.SecureToken, signingKey *rsa.PrivateKey) (string, error)

DeriveRootToken derives a permanent admin JWT from the bootstrap token using HKDF.

The root token provides unrestricted API access and never expires. It's intended for break-glass admin access and initial service account creation.

Derivation properties:

  • Deterministic: same bootstrap token always produces same root token
  • Domain-separated: uses salt "neuwerk-root-token-v1" to prevent cross-domain attacks
  • RFC 5869 compliant: uses HKDF with SHA-256

JWT claims:

  • sub: "root" (root user)
  • role: "admin" (full access)
  • type: "root" (token type)
  • iat: issued at timestamp
  • jti: derived from HKDF output (for tracking)
  • NO exp: root tokens never expire

Security notes:

  • New bootstrap token generates new root token (one-to-one binding)
  • Root token should only be used for initial setup or emergency access
  • Tokens should be stored securely and never logged

Parameters:

  • bootstrapToken: 256-bit bootstrap token (base64-encoded)
  • signingKey: RSA 4096-bit private key for JWT signing

Returns:

  • Signed JWT token string (ready for Authorization header)
  • Error if derivation or signing fails

func GenerateSigningKeys

func GenerateSigningKeys() (*rsa.PrivateKey, error)

GenerateSigningKeys generates an RSA 4096-bit key pair for JWT signing Returns the private key (public key accessible via privateKey.Public()) Matches Phase 17 CA key parameters for long-term security

func LoadPrivateKey

func LoadPrivateKey(path string) (*rsa.PrivateKey, error)

LoadPrivateKey loads an RSA private key from a PEM-encoded file Verifies the PEM block type matches "RSA PRIVATE KEY" and parses PKCS1 format

func SavePrivateKey

func SavePrivateKey(key *rsa.PrivateKey, path string) error

SavePrivateKey saves an RSA private key to disk in PEM format with 0600 permissions The key is marshaled using PKCS1 format ("RSA PRIVATE KEY" block type) Parent directory must exist before calling this function

func SignToken

func SignToken(claims jwt.MapClaims, privateKey *rsa.PrivateKey) (string, error)

SignToken creates a JWT token signed with RS256 algorithm Claims are passed as jwt.MapClaims for flexibility Returns the signed JWT string ready for transmission

func VerifyToken

func VerifyToken(tokenString string, publicKey *rsa.PublicKey) (jwt.MapClaims, error)

VerifyToken verifies a JWT token signature and extracts claims Enforces RS256 algorithm to prevent algorithm confusion attacks Validates expiration if present (root tokens have no expiration) Returns extracted claims on successful verification

Types

type AuditStore

type AuditStore interface {
	RecordAuthEvent(entry interface{}) error
}

AuditStore interface for authentication event logging (Phase 29)

type AuthConfig

type AuthConfig struct {
	// JWT configuration (signing keys)
	JWT JWTConfig `yaml:"jwt"`

	// Providers is the list of OIDC/OAuth providers
	Providers []Provider `yaml:"providers"`

	// RoleMappings defines how provider claims map to Neuwerk roles
	RoleMappings []RoleMapping `yaml:"role_mappings"`
}

AuthConfig is the top-level configuration for authentication

func LoadConfig

func LoadConfig(path string) (*AuthConfig, error)

LoadConfig loads and validates authentication configuration from YAML file Environment variables override secrets with pattern: NEUWERK_AUTH_{TYPE}_CLIENT_SECRET

func (*AuthConfig) String

func (c *AuthConfig) String() string

String implements fmt.Stringer with secret redaction for safe logging

func (*AuthConfig) Validate

func (c *AuthConfig) Validate() error

Validate checks AuthConfig for required fields and valid values

type ConfigManager

type ConfigManager struct {
	// contains filtered or unexported fields
}

ConfigManager manages auth configuration with hot-reload support

func NewConfigManager

func NewConfigManager(path string) (*ConfigManager, error)

NewConfigManager creates a new config manager with hot-reload support Loads initial config and sets up fsnotify watcher for file changes

func (*ConfigManager) Close

func (m *ConfigManager) Close() error

Close stops the file watcher

func (*ConfigManager) Get

func (m *ConfigManager) Get() *AuthConfig

Get returns the current configuration (thread-safe)

func (*ConfigManager) Start

func (m *ConfigManager) Start(ctx context.Context)

Start begins the file watch loop for hot-reload Runs in background goroutine until context is canceled

type ErrorResponse

type ErrorResponse struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

ErrorResponse represents an error response in JSON format

type JWTConfig

type JWTConfig struct {
	// SigningKeyPath is the path to RSA private key (PEM format)
	SigningKeyPath string `yaml:"signing_key_path"`

	// PublicKeyPath is the path to RSA public key (PEM format, optional - derived from private)
	PublicKeyPath string `yaml:"public_key_path,omitempty"`
}

JWTConfig contains JWT signing key configuration

type Middleware

type Middleware struct {
	// contains filtered or unexported fields
}

Middleware provides JWT verification and authorization middleware for Chi

func NewMiddleware

func NewMiddleware(publicKey *rsa.PublicKey) *Middleware

NewMiddleware creates a new JWT middleware with the provided RSA public key

func (*Middleware) JWTAuthenticator

func (m *Middleware) JWTAuthenticator(next http.Handler) http.Handler

JWTAuthenticator enforces that a valid JWT is present in the request context. This middleware should be used after JWTVerifier in the middleware chain. It rejects requests with missing or invalid tokens with a 401 error.

For service account tokens (type:"service-account"), this middleware also:

  • Validates the service account has not been revoked
  • Tracks usage metadata (last used, IP, endpoints) with 5-minute throttle

func (*Middleware) JWTVerifier

func (m *Middleware) JWTVerifier(next http.Handler) http.Handler

JWTVerifier extracts the JWT token from the Authorization Bearer header and verifies it using the public key. It stores the claims (or error) in the request context for downstream middleware to handle.

This middleware DOES NOT reject requests - it only extracts and verifies. Use JWTAuthenticator to enforce authentication.

func (*Middleware) RequireRole

func (m *Middleware) RequireRole(allowedRoles ...string) func(http.Handler) http.Handler

RequireRole returns a middleware that enforces role-based access control. It checks if the JWT claims contain a role that matches one of the allowed roles. Rejects requests with insufficient permissions with a 403 error.

func (*Middleware) SetAuditStore

func (m *Middleware) SetAuditStore(store AuditStore)

SetAuditStore sets the audit store for authentication event logging (Phase 29). This method should be called after middleware construction when the store becomes available.

func (*Middleware) SetServiceAccountStore

func (m *Middleware) SetServiceAccountStore(store ServiceAccountStoreInterface)

SetServiceAccountStore sets the service account store for revocation checking and usage tracking. This method should be called after middleware construction when the store becomes available. The middleware will check token revocation and track usage for service account tokens.

func (*Middleware) VerifyToken

func (m *Middleware) VerifyToken(tokenString string) (jwt.MapClaims, error)

VerifyToken verifies a JWT token and returns its claims. This is a convenience method that wraps the standalone VerifyToken function.

type Provider

type Provider struct {
	// Type identifies the provider: "google", "github", "azure", "okta"
	Type string `yaml:"type"`

	// Name is the display name for UI
	Name string `yaml:"name"`

	// ClientID is the OAuth client ID
	ClientID string `yaml:"client_id"`

	// ClientSecret is the OAuth client secret (prefer environment variable override)
	ClientSecret string `yaml:"client_secret,omitempty"`

	// IssuerURL is the OIDC issuer URL (for discovery)
	IssuerURL string `yaml:"issuer_url"`

	// RedirectURL is the OAuth redirect URL
	RedirectURL string `yaml:"redirect_url"`

	// Scopes are the OAuth scopes to request
	Scopes []string `yaml:"scopes"`
}

Provider defines an OIDC or OAuth provider configuration

type RoleMapping

type RoleMapping struct {
	// Provider is the provider type this mapping applies to
	Provider string `yaml:"provider"`

	// AdminEmails defines email addresses that grant admin role
	AdminEmails []string `yaml:"admin_emails"`

	// AdminGroups defines group names that grant admin role
	AdminGroups []string `yaml:"admin_groups"`

	// ReadonlyGroups defines group names that grant readonly role
	ReadonlyGroups []string `yaml:"readonly_groups"`
}

RoleMapping defines how provider claims map to Neuwerk roles

type ServiceAccountStoreInterface

type ServiceAccountStoreInterface interface {
	// Get retrieves a service account by ID
	// Returns jetstream.ErrKeyNotFound if the service account is revoked
	Get(ctx context.Context, id string) (interface{}, error)

	// UpdateUsage updates the service account's usage tracking fields
	// Updates LastUsed, LastIP, and LastEndpoints (last 10 unique, FIFO)
	UpdateUsage(ctx context.Context, id, ip, endpoint string) error
}

ServiceAccountStoreInterface defines the contract for service account storage operations. This interface enables clean dependency injection without import cycles. Implementations must provide:

  • Get: Retrieve service account by ID (returns error if revoked/not found)
  • UpdateUsage: Track last used timestamp, IP, and endpoint

The middleware uses this interface to:

  • Check if a service account token has been revoked
  • Track usage metadata for audit purposes

type SessionMiddleware

type SessionMiddleware struct {
	// contains filtered or unexported fields
}

SessionMiddleware provides cookie-based session authentication with sliding window TTL.

func NewSessionMiddleware

func NewSessionMiddleware(store SessionStore) *SessionMiddleware

NewSessionMiddleware creates a new SessionMiddleware with the provided session store.

func (*SessionMiddleware) CreateUserSession

func (m *SessionMiddleware) CreateUserSession(ctx context.Context, data session.UserSession, ttl time.Duration) (string, error)

CreateUserSession creates a new authenticated user session. This delegates to the underlying store if it supports session creation. Returns an error if the store doesn't support CreateUserSession.

func (*SessionMiddleware) ExtendSession

func (m *SessionMiddleware) ExtendSession(next http.Handler) http.Handler

ExtendSession is a middleware that extracts the session cookie, validates the session, and refreshes the session TTL if needed (sliding window expiration).

Flow:

  1. Extract "neuwerk_session" cookie
  2. If no cookie or empty, pass to next handler (may be public route or Bearer token auth)
  3. Get session from store
  4. If session not found/expired, clear cookie and redirect to login
  5. Check if session exceeds 7-day limit for non-RememberMe sessions
  6. Refresh session TTL if LastActive > 5 minutes ago (throttled updates)
  7. Store session in context as both UserSession and JWT claims (RequireRole compatibility)
  8. Call next handler

type SessionStore

type SessionStore interface {
	GetUserSession(ctx context.Context, sessionID string) (*session.UserSession, error)
	UpdateUserSession(ctx context.Context, sessionID string, userSession session.UserSession, ttl time.Duration) error
}

SessionStore interface defines the operations needed for user session management. This allows the middleware to work with any session storage implementation.

type UserSessionCreator

type UserSessionCreator interface {
	CreateUserSession(ctx context.Context, data session.UserSession, ttl time.Duration) (string, error)
}

UserSessionCreator is an extended interface for session stores that support user session creation. This is used by token-login to create browser sessions from API tokens.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL