# ResolveSpec Security Provider

Type-safe, composable security system for ResolveSpec with support for authentication, column-level security (masking), and row-level security (filtering).

## Features

- ✅ **Interface-Based** - Type-safe providers instead of callbacks
- ✅ **Login/Logout Support** - Built-in authentication lifecycle
- ✅ **Composable** - Mix and match different providers
- ✅ **No Global State** - Each handler has its own security configuration
- ✅ **Testable** - Easy to mock and test
- ✅ **Extensible** - Implement custom providers for your needs
- ✅ **Stored Procedures** - All database operations use PostgreSQL stored procedures for security and maintainability

## Stored Procedure Architecture

**All database-backed security providers use PostgreSQL stored procedures exclusively.** No raw SQL queries are executed from Go code.

### Benefits

- **Security**: Database logic is centralized and protected
- **Maintainability**: Update database logic without recompiling Go code
- **Performance**: Stored procedures are pre-compiled and optimized
- **Testability**: Test database logic independently
- **Consistency**: Standardized `resolvespec_*` naming convention

### Available Stored Procedures

| Procedure | Purpose | Used By |
|-----------|---------|---------|
| `resolvespec_login` | Session-based login | DatabaseAuthenticator |
| `resolvespec_logout` | Session invalidation | DatabaseAuthenticator |
| `resolvespec_session` | Session validation | DatabaseAuthenticator |
| `resolvespec_session_update` | Update session activity | DatabaseAuthenticator |
| `resolvespec_refresh_token` | Token refresh | DatabaseAuthenticator |
| `resolvespec_jwt_login` | JWT user validation | JWTAuthenticator |
| `resolvespec_jwt_logout` | JWT token blacklist | JWTAuthenticator |
| `resolvespec_column_security` | Load column rules | DatabaseColumnSecurityProvider |
| `resolvespec_row_security` | Load row templates | DatabaseRowSecurityProvider |

See `database_schema.sql` for complete stored procedure definitions and examples.

## Quick Start

```go
import (
    "github.com/bitechdev/ResolveSpec/pkg/security"
    "github.com/bitechdev/ResolveSpec/pkg/restheadspec"
)

// 1. Create security providers
auth := security.NewJWTAuthenticator("your-secret-key", db)
colSec := security.NewDatabaseColumnSecurityProvider(db)
rowSec := security.NewDatabaseRowSecurityProvider(db)

// 2. Combine providers
provider := security.NewCompositeSecurityProvider(auth, colSec, rowSec)

// 3. Create handler and register security hooks
handler := restheadspec.NewHandlerWithGORM(db)
securityList := security.NewSecurityList(provider)
restheadspec.RegisterSecurityHooks(handler, securityList)

// 4. Apply middleware
router := mux.NewRouter()
restheadspec.SetupMuxRoutes(router, handler)
router.Use(security.NewAuthMiddleware(securityList))
router.Use(security.SetSecurityMiddleware(securityList))
```

## Architecture

### Spec-Agnostic Design

The security system is **completely spec-agnostic** - it doesn't depend on any specific spec implementation. Instead, each spec (restheadspec, funcspec, resolvespec) implements its own security integration by adapting to the `SecurityContext` interface.

```
┌─────────────────────────────────────┐
│     Security Package (Generic)      │
│  - SecurityContext interface        │
│  - Security providers                │
│  - Core security logic               │
└─────────────────────────────────────┘
           ▲          ▲          ▲
           │          │          │
    ┌──────┘          │          └──────┐
    │                 │                 │
┌───▼────┐      ┌────▼─────┐     ┌────▼──────┐
│RestHead│      │ FuncSpec │     │ResolveSpec│
│  Spec  │      │          │     │           │
│        │      │          │     │           │
│Adapts  │      │ Adapts   │     │  Adapts   │
│to      │      │ to       │     │  to       │
│Security│      │ Security │     │  Security │
│Context │      │ Context  │     │  Context  │
└────────┘      └──────────┘     └───────────┘
```

**Benefits:**
- ✅ No circular dependencies
- ✅ Each spec can customize security integration
- ✅ Easy to add new specs
- ✅ Security logic is reusable across all specs

### Core Interfaces

The security system is built on three main interfaces:

#### 1. Authenticator
Handles user authentication lifecycle:

```go
type Authenticator interface {
    Login(ctx context.Context, req LoginRequest) (*LoginResponse, error)
    Logout(ctx context.Context, req LogoutRequest) error
    Authenticate(r *http.Request) (*UserContext, error)
}
```

#### 2. ColumnSecurityProvider
Manages column-level security (masking/hiding):

```go
type ColumnSecurityProvider interface {
    GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]ColumnSecurity, error)
}
```

#### 3. RowSecurityProvider
Manages row-level security (WHERE clause filtering):

```go
type RowSecurityProvider interface {
    GetRowSecurity(ctx context.Context, userID int, schema, table string) (RowSecurity, error)
}
```

### SecurityProvider
The main interface that combines all three:

```go
type SecurityProvider interface {
    Authenticator
    ColumnSecurityProvider
    RowSecurityProvider
}
```

#### 4. SecurityContext (Spec Integration Interface)
Each spec implements this interface to integrate with the security system:

```go
type SecurityContext interface {
    GetContext() context.Context
    GetUserID() (int, bool)
    GetSchema() string
    GetEntity() string
    GetModel() interface{}
    GetQuery() interface{}
    SetQuery(interface{})
    GetResult() interface{}
    SetResult(interface{})
}
```

**Implementation Examples:**
- `restheadspec`: Adapts `restheadspec.HookContext` → `SecurityContext`
- `funcspec`: Adapts `funcspec.HookContext` → `SecurityContext`
- `resolvespec`: Adapts `resolvespec.HookContext` → `SecurityContext`

### UserContext
Enhanced user context with complete user information:

```go
type UserContext struct {
    UserID    int            // User's unique ID
    UserName  string         // Username
    UserLevel int            // User privilege level
    SessionID string         // Current session ID
    RemoteID  string         // Remote system ID
    Roles     []string       // User roles
    Email     string         // User email
    Claims    map[string]any // Additional authentication claims
    Meta      map[string]any // Additional metadata (can hold any JSON-serializable values)
}
```

## Available Implementations

### Authenticators

**HeaderAuthenticator** - Simple header-based authentication:
```go
auth := security.NewHeaderAuthenticator()
// Expects: X-User-ID, X-User-Name, X-User-Level, etc.
```

**DatabaseAuthenticator** - Database session-based authentication (Recommended):
```go
auth := security.NewDatabaseAuthenticator(db)
// Supports: Login, Logout, Session management, Token refresh
// All operations use stored procedures: resolvespec_login, resolvespec_logout,
// resolvespec_session, resolvespec_session_update, resolvespec_refresh_token
// Requires: users and user_sessions tables + stored procedures (see database_schema.sql)
```

**JWTAuthenticator** - JWT token authentication with login/logout:
```go
auth := security.NewJWTAuthenticator("secret-key", db)
// Supports: Login, Logout, JWT token validation
// All operations use stored procedures: resolvespec_jwt_login, resolvespec_jwt_logout
// Note: Requires JWT library installation for token signing/verification
```

### Column Security Providers

**DatabaseColumnSecurityProvider** - Loads rules from database:
```go
colSec := security.NewDatabaseColumnSecurityProvider(db)
// Uses stored procedure: resolvespec_column_security
// Queries core.secaccess and core.hub_link tables
```

**ConfigColumnSecurityProvider** - Static configuration:
```go
rules := map[string][]security.ColumnSecurity{
    "public.employees": {
        {Path: []string{"ssn"}, Accesstype: "mask", MaskStart: 5},
    },
}
colSec := security.NewConfigColumnSecurityProvider(rules)
```

### Row Security Providers

**DatabaseRowSecurityProvider** - Loads filters from database:
```go
rowSec := security.NewDatabaseRowSecurityProvider(db)
// Uses stored procedure: resolvespec_row_security
```

**ConfigRowSecurityProvider** - Static templates:
```go
templates := map[string]string{
    "public.orders": "user_id = {UserID}",
}
blocked := map[string]bool{
    "public.admin_logs": true,
}
rowSec := security.NewConfigRowSecurityProvider(templates, blocked)
```

## Usage Examples

### Example 1: Complete Database-Backed Security with Sessions (restheadspec)

```go
func main() {
    db := setupDatabase()

    // Run migrations (see database_schema.sql)
    // db.Exec("CREATE TABLE users ...")
    // db.Exec("CREATE TABLE user_sessions ...")

    // Create handler
    handler := restheadspec.NewHandlerWithGORM(db)

    // Create security providers
    auth := security.NewDatabaseAuthenticator(db) // Session-based auth
    colSec := security.NewDatabaseColumnSecurityProvider(db)
    rowSec := security.NewDatabaseRowSecurityProvider(db)

    // Combine providers
    provider := security.NewCompositeSecurityProvider(auth, colSec, rowSec)
    securityList := security.NewSecurityList(provider)

    // Register security hooks for this spec
    restheadspec.RegisterSecurityHooks(handler, securityList)

    // Setup routes
    router := mux.NewRouter()

    // Add auth endpoints
    router.HandleFunc("/auth/login", handleLogin(securityList)).Methods("POST")
    router.HandleFunc("/auth/logout", handleLogout(securityList)).Methods("POST")
    router.HandleFunc("/auth/refresh", handleRefresh(securityList)).Methods("POST")

    // Setup API with security
    apiRouter := router.PathPrefix("/api").Subrouter()
    restheadspec.SetupMuxRoutes(apiRouter, handler)
    apiRouter.Use(security.NewAuthMiddleware(securityList))
    apiRouter.Use(security.SetSecurityMiddleware(securityList))

    http.ListenAndServe(":8080", router)
}

func handleLogin(securityList *security.SecurityList) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        var req security.LoginRequest
        json.NewDecoder(r.Body).Decode(&req)

        // Add client info to claims
        req.Claims = map[string]any{
            "ip_address": r.RemoteAddr,
            "user_agent": r.UserAgent(),
        }

        resp, err := securityList.Provider().Login(r.Context(), req)
        if err != nil {
            http.Error(w, err.Error(), http.StatusUnauthorized)
            return
        }

        // Set session cookie (optional)
        http.SetCookie(w, &http.Cookie{
            Name:     "session_token",
            Value:    resp.Token,
            Expires:  time.Now().Add(24 * time.Hour),
            HttpOnly: true,
            Secure:   true, // Use in production with HTTPS
            SameSite: http.SameSiteStrictMode,
        })

        json.NewEncoder(w).Encode(resp)
    }
}

func handleRefresh(securityList *security.SecurityList) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("X-Refresh-Token")

        if refreshable, ok := securityList.Provider().(security.Refreshable); ok {
            resp, err := refreshable.RefreshToken(r.Context(), token)
            if err != nil {
                http.Error(w, err.Error(), http.StatusUnauthorized)
                return
            }
            json.NewEncoder(w).Encode(resp)
        } else {
            http.Error(w, "Refresh not supported", http.StatusNotImplemented)
        }
    }
}
```

### Example 2: Config-Based Security (No Database)

```go
func main() {
    db := setupDatabase()
    handler := restheadspec.NewHandlerWithGORM(db)

    // Static column security rules
    columnRules := map[string][]security.ColumnSecurity{
        "public.employees": {
            {Path: []string{"ssn"}, Accesstype: "mask", MaskStart: 5},
            {Path: []string{"salary"}, Accesstype: "hide"},
        },
    }

    // Static row security templates
    rowTemplates := map[string]string{
        "public.orders": "user_id = {UserID}",
    }

    // Create providers
    auth := security.NewHeaderAuthenticator()
    colSec := security.NewConfigColumnSecurityProvider(columnRules)
    rowSec := security.NewConfigRowSecurityProvider(rowTemplates, nil)

    // Combine providers and register hooks
    provider := security.NewCompositeSecurityProvider(auth, colSec, rowSec)
    securityList := security.NewSecurityList(provider)
    restheadspec.RegisterSecurityHooks(handler, securityList)

    // Setup routes...
}
```

### Example 3: FuncSpec Security (SQL Query API)

```go
import (
    "github.com/bitechdev/ResolveSpec/pkg/funcspec"
    "github.com/bitechdev/ResolveSpec/pkg/security"
)

func main() {
    db := setupDatabase()

    // Create funcspec handler
    handler := funcspec.NewHandler(db)

    // Create security providers
    auth := security.NewJWTAuthenticator("secret-key", db)
    colSec := security.NewDatabaseColumnSecurityProvider(db)
    rowSec := security.NewDatabaseRowSecurityProvider(db)

    // Combine providers
    provider := security.NewCompositeSecurityProvider(auth, colSec, rowSec)
    securityList := security.NewSecurityList(provider)

    // Register security hooks (audit logging)
    funcspec.RegisterSecurityHooks(handler, securityList)

    // Note: funcspec operates on raw SQL queries, so row/column
    // security is limited. Security should be enforced at the
    // SQL function level or via database policies.

    // Setup routes...
}
```

### Example 4: ResolveSpec Security (REST API)

```go
import (
    "github.com/bitechdev/ResolveSpec/pkg/resolvespec"
    "github.com/bitechdev/ResolveSpec/pkg/security"
)

func main() {
    db := setupDatabase()
    registry := common.NewModelRegistry()

    // Register models
    registry.RegisterModel("public.users", &User{})
    registry.RegisterModel("public.orders", &Order{})

    // Create resolvespec handler
    handler := resolvespec.NewHandler(db, registry)

    // Create security providers
    auth := security.NewDatabaseAuthenticator(db)
    colSec := security.NewDatabaseColumnSecurityProvider(db)
    rowSec := security.NewDatabaseRowSecurityProvider(db)

    // Combine providers
    provider := security.NewCompositeSecurityProvider(auth, colSec, rowSec)
    securityList := security.NewSecurityList(provider)

    // Register security hooks for resolvespec
    resolvespec.RegisterSecurityHooks(handler, securityList)

    // Setup routes...
}
```

### Example 5: Custom Provider

Implement your own provider for complete control:

```go
type MySecurityProvider struct {
    db *gorm.DB
}

func (p *MySecurityProvider) Login(ctx context.Context, req security.LoginRequest) (*security.LoginResponse, error) {
    // Your custom login logic
}

func (p *MySecurityProvider) Logout(ctx context.Context, req security.LogoutRequest) error {
    // Your custom logout logic
}

func (p *MySecurityProvider) Authenticate(r *http.Request) (*security.UserContext, error) {
    // Your custom authentication logic
}

func (p *MySecurityProvider) GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]security.ColumnSecurity, error) {
    // Your custom column security logic
}

func (p *MySecurityProvider) GetRowSecurity(ctx context.Context, userID int, schema, table string) (security.RowSecurity, error) {
    // Your custom row security logic
}

// Use it with any spec
provider := &MySecurityProvider{db: db}
securityList := security.NewSecurityList(provider)

// Register with restheadspec
restheadspec.RegisterSecurityHooks(restHandler, securityList)

// Or with funcspec
funcspec.RegisterSecurityHooks(funcHandler, securityList)

// Or with resolvespec
resolvespec.RegisterSecurityHooks(resolveHandler, securityList)
```

## Security Features

### Column Security (Masking/Hiding)

**Mask SSN (show last 4 digits):**
```go
{
    Path:       []string{"ssn"},
    Accesstype: "mask",
    MaskStart:  5,
    MaskChar:   "*",
}
// "123-45-6789" → "*****6789"
```

**Hide entire field:**
```go
{
    Path:       []string{"salary"},
    Accesstype: "hide",
}
// Field returns 0 or empty
```

**Nested JSON field masking:**
```go
{
    Path:       []string{"address", "street"},
    Accesstype: "mask",
    MaskStart:  10,
}
```

### Row Security (Filtering)

**User isolation:**
```go
{
    Template: "user_id = {UserID}",
}
// Users only see their own records
```

**Tenant isolation:**
```go
{
    Template: "tenant_id = {TenantID} AND user_id = {UserID}",
}
```

**Block all access:**
```go
{
    HasBlock: true,
}
// Completely blocks access to the table
```

**Template variables:**
- `{UserID}` - Current user's ID
- `{PrimaryKeyName}` - Primary key column
- `{TableName}` - Table name
- `{SchemaName}` - Schema name

## Request Flow

```
HTTP Request
    ↓
NewAuthMiddleware (security package)
    ├─ Calls provider.Authenticate(request)
    └─ Adds UserContext to context
    ↓
SetSecurityMiddleware (security package)
    └─ Adds SecurityList to context
    ↓
Spec Handler (restheadspec/funcspec/resolvespec)
    ↓
BeforeRead Hook (registered by spec)
    ├─ Adapts spec's HookContext → SecurityContext
    ├─ Calls security.LoadSecurityRules(secCtx, securityList)
    │   ├─ Calls provider.GetColumnSecurity()
    │   └─ Calls provider.GetRowSecurity()
    └─ Caches security rules
    ↓
BeforeScan Hook (registered by spec)
    ├─ Adapts spec's HookContext → SecurityContext
    ├─ Calls security.ApplyRowSecurity(secCtx, securityList)
    └─ Applies row security (adds WHERE clause to query)
    ↓
Database Query (with security filters)
    ↓
AfterRead Hook (registered by spec)
    ├─ Adapts spec's HookContext → SecurityContext
    ├─ Calls security.ApplyColumnSecurity(secCtx, securityList)
    ├─ Applies column security (masks/hides fields)
    └─ Calls security.LogDataAccess(secCtx)
    ↓
HTTP Response (secured data)
```

**Key Points:**
- Security package is spec-agnostic and provides core logic
- Each spec registers its own hooks that adapt to SecurityContext
- Security rules are loaded once and cached for the request
- Row security is applied to the query (database level)
- Column security is applied to results (application level)

## Testing

The interface-based design makes testing straightforward:

```go
// Mock authenticator for tests
type MockAuthenticator struct {
    UserToReturn *security.UserContext
    ErrorToReturn error
}

func (m *MockAuthenticator) Authenticate(r *http.Request) (*security.UserContext, error) {
    return m.UserToReturn, m.ErrorToReturn
}

// Use in tests
func TestMyHandler(t *testing.T) {
    mockAuth := &MockAuthenticator{
        UserToReturn: &security.UserContext{UserID: 123},
    }

    provider := security.NewCompositeSecurityProvider(
        mockAuth,
        &MockColumnSecurity{},
        &MockRowSecurity{},
    )

    securityList := security.SetupSecurityProvider(handler, provider)
    // ... test your handler
}
```

## Migration Guide

### From Old Callback System

If you're upgrading from the old callback-based system:

**Old:**
```go
security.GlobalSecurity.AuthenticateCallback = myAuthFunc
security.GlobalSecurity.LoadColumnSecurityCallback = myColSecFunc
security.GlobalSecurity.LoadRowSecurityCallback = myRowSecFunc
security.SetupSecurityProvider(handler, &security.GlobalSecurity)
```

**New:**
```go
// 1. Wrap your functions in a provider
type MyProvider struct{}

func (p *MyProvider) Authenticate(r *http.Request) (*security.UserContext, error) {
    userID, roles, err := myAuthFunc(r)
    return &security.UserContext{UserID: userID, Roles: strings.Split(roles, ",")}, err
}

func (p *MyProvider) GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]security.ColumnSecurity, error) {
    return myColSecFunc(userID, schema, table)
}

func (p *MyProvider) GetRowSecurity(ctx context.Context, userID int, schema, table string) (security.RowSecurity, error) {
    return myRowSecFunc(userID, schema, table)
}

func (p *MyProvider) Login(ctx context.Context, req security.LoginRequest) (*security.LoginResponse, error) {
    return nil, fmt.Errorf("not implemented")
}

func (p *MyProvider) Logout(ctx context.Context, req security.LogoutRequest) error {
    return nil
}

// 2. Create security list and register hooks
provider := &MyProvider{}
securityList := security.NewSecurityList(provider)

// 3. Register with your spec
restheadspec.RegisterSecurityHooks(handler, securityList)
```

### From Old SetupSecurityProvider API

If you're upgrading from the previous interface-based system:

**Old:**
```go
securityList := security.SetupSecurityProvider(handler, provider)
```

**New:**
```go
securityList := security.NewSecurityList(provider)
restheadspec.RegisterSecurityHooks(handler, securityList) // or funcspec/resolvespec
```

The main changes:
1. Security package no longer knows about specific spec types
2. Each spec registers its own security hooks
3. More flexible - same security provider works with all specs

## Documentation

| File | Description |
|------|-------------|
| **QUICK_REFERENCE.md** | Quick reference guide with examples |
| **INTERFACE_GUIDE.md** | Complete implementation guide |
| **examples.go** | Working provider implementations |
| **setup_example.go** | 6 complete integration examples |

## API Reference

### Context Helpers

Get user information from request context:

```go
userCtx, ok := security.GetUserContext(ctx)
userID, ok := security.GetUserID(ctx)
userName, ok := security.GetUserName(ctx)
userLevel, ok := security.GetUserLevel(ctx)
sessionID, ok := security.GetSessionID(ctx)
remoteID, ok := security.GetRemoteID(ctx)
roles, ok := security.GetUserRoles(ctx)
email, ok := security.GetUserEmail(ctx)
```

### Optional Interfaces

Implement these for additional features:

**Refreshable** - Token refresh support:
```go
type Refreshable interface {
    RefreshToken(ctx context.Context, refreshToken string) (*LoginResponse, error)
}
```

**Validatable** - Token validation:
```go
type Validatable interface {
    ValidateToken(ctx context.Context, token string) (bool, error)
}
```

**Cacheable** - Cache management:
```go
type Cacheable interface {
    ClearCache(ctx context.Context, userID int, schema, table string) error
}
```

## Benefits Over Callbacks

| Feature | Old (Callbacks) | New (Interfaces) |
|---------|----------------|------------------|
| Type Safety | ❌ Callbacks can be nil | ✅ Compile-time verification |
| Global State | ❌ GlobalSecurity variable | ✅ Dependency injection |
| Testability | ⚠️ Need to set globals | ✅ Easy to mock |
| Composability | ❌ Single provider only | ✅ Mix and match |
| Login/Logout | ❌ Not supported | ✅ Built-in |
| Extensibility | ⚠️ Limited | ✅ Optional interfaces |

## Common Patterns

### Caching Security Rules

```go
type CachedProvider struct {
    inner security.ColumnSecurityProvider
    cache *cache.Cache
}

func (p *CachedProvider) GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]security.ColumnSecurity, error) {
    key := fmt.Sprintf("%d:%s.%s", userID, schema, table)
    if cached, found := p.cache.Get(key); found {
        return cached.([]security.ColumnSecurity), nil
    }

    rules, err := p.inner.GetColumnSecurity(ctx, userID, schema, table)
    if err == nil {
        p.cache.Set(key, rules, cache.DefaultExpiration)
    }
    return rules, err
}
```

### Role-Based Security

```go
func (p *MyProvider) GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]security.ColumnSecurity, error) {
    userCtx, _ := security.GetUserContext(ctx)

    if contains(userCtx.Roles, "admin") {
        return []security.ColumnSecurity{}, nil // No restrictions
    }

    return loadRestrictionsForUser(userID, schema, table), nil
}
```

### Multi-Tenant Isolation

```go
func (p *MyProvider) GetRowSecurity(ctx context.Context, userID int, schema, table string) (security.RowSecurity, error) {
    tenantID := getUserTenant(userID)

    return security.RowSecurity{
        Template: fmt.Sprintf("tenant_id = %d AND user_id = {UserID}", tenantID),
    }, nil
}
```

## Middleware and Handler API

### NewAuthMiddleware
Standard middleware that authenticates all requests:

```go
router.Use(security.NewAuthMiddleware(securityList))
```

Routes can skip authentication using the `SkipAuth` helper:

```go
func PublicHandler(w http.ResponseWriter, r *http.Request) {
    ctx := security.SkipAuth(r.Context())
    // This route will bypass authentication
    // A guest user context will be set instead
}

router.Handle("/public", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    ctx := security.SkipAuth(r.Context())
    PublicHandler(w, r.WithContext(ctx))
}))
```

When authentication is skipped, a guest user context is automatically set:
- UserID: 0
- UserName: "guest"
- Roles: ["guest"]
- RemoteID: Request's remote address

Routes can use optional authentication with the `OptionalAuth` helper:

```go
func OptionalAuthHandler(w http.ResponseWriter, r *http.Request) {
    ctx := security.OptionalAuth(r.Context())
    r = r.WithContext(ctx)

    // This route will try to authenticate
    // If authentication succeeds, authenticated user context is set
    // If authentication fails, guest user context is set instead

    userCtx, _ := security.GetUserContext(r.Context())
    if userCtx.UserID == 0 {
        // Guest user
        fmt.Fprintf(w, "Welcome, guest!")
    } else {
        // Authenticated user
        fmt.Fprintf(w, "Welcome back, %s!", userCtx.UserName)
    }
}

router.Handle("/home", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    ctx := security.OptionalAuth(r.Context())
    OptionalAuthHandler(w, r.WithContext(ctx))
}))
```

**Authentication Modes Summary:**
- **Required (default)**: Authentication must succeed or returns 401
- **SkipAuth**: Bypasses authentication entirely, always sets guest context
- **OptionalAuth**: Tries authentication, falls back to guest context if it fails

### NewAuthHandler

Standalone authentication handler (without middleware wrapping):

```go
// Use when you need authentication logic without middleware
authHandler := security.NewAuthHandler(securityList, myHandler)
http.Handle("/api/protected", authHandler)
```

### NewOptionalAuthHandler

Standalone optional authentication handler that tries to authenticate but falls back to guest:

```go
// Use for routes that should work for both authenticated and guest users
optionalHandler := security.NewOptionalAuthHandler(securityList, myHandler)
http.Handle("/home", optionalHandler)

// Example handler that checks user context
func myHandler(w http.ResponseWriter, r *http.Request) {
    userCtx, _ := security.GetUserContext(r.Context())
    if userCtx.UserID == 0 {
        fmt.Fprintf(w, "Welcome, guest!")
    } else {
        fmt.Fprintf(w, "Welcome back, %s!", userCtx.UserName)
    }
}
```

### Helper Functions

Extract user information from context:

```go
// Get full user context
userCtx, ok := security.GetUserContext(ctx)

// Get specific fields
userID, ok := security.GetUserID(ctx)
userName, ok := security.GetUserName(ctx)
userLevel, ok := security.GetUserLevel(ctx)
sessionID, ok := security.GetSessionID(ctx)
remoteID, ok := security.GetRemoteID(ctx)
roles, ok := security.GetUserRoles(ctx)
email, ok := security.GetUserEmail(ctx)
meta, ok := security.GetUserMeta(ctx)
```

### Metadata Support

The `Meta` field in `UserContext` can hold any JSON-serializable values:

```go
// Set metadata during login
loginReq := security.LoginRequest{
    Username: "user@example.com",
    Password: "password",
    Meta: map[string]any{
        "department": "engineering",
        "location": "US",
        "preferences": map[string]any{
            "theme": "dark",
        },
    },
}

// Access metadata in handlers
meta, ok := security.GetUserMeta(ctx)
if ok {
    department := meta["department"].(string)
}
```

## License

Part of the ResolveSpec project.
