Auth Extension

Comprehensive authentication and authorization with multiple providers and security features

Auth Extension

The Auth extension provides comprehensive authentication and authorization capabilities with support for multiple providers, middleware integration, and advanced security features.

The Auth extension supports JWT, OAuth2, SAML, LDAP, and custom authentication providers with unified interfaces.

Features

Authentication Providers

  • JWT: JSON Web Token authentication
  • OAuth2: OAuth 2.0 with multiple providers (Google, GitHub, etc.)
  • SAML: Security Assertion Markup Language
  • LDAP: Lightweight Directory Access Protocol
  • Basic Auth: Username/password authentication
  • API Keys: Token-based authentication
  • Custom: Build your own authentication provider

Security Features

  • Multi-Factor Authentication (MFA): TOTP, SMS, Email
  • Session Management: Secure session handling
  • Rate Limiting: Prevent brute force attacks
  • Password Policies: Enforce strong passwords
  • Account Lockout: Temporary account suspension
  • Audit Logging: Track authentication events

Authorization

  • Role-Based Access Control (RBAC): Define roles and permissions
  • Attribute-Based Access Control (ABAC): Fine-grained permissions
  • Resource-Based Permissions: Control access to specific resources
  • Dynamic Permissions: Runtime permission evaluation

Installation

go get github.com/xraph/forge/extensions/auth

Configuration

extensions:
  auth:
    # Default provider
    default_provider: "jwt"
    
    # JWT Configuration
    jwt:
      secret: "${JWT_SECRET}"
      issuer: "forge-app"
      audience: "forge-users"
      expiry: "24h"
      refresh_expiry: "7d"
      algorithm: "HS256"
    
    # OAuth2 Configuration
    oauth2:
      providers:
        google:
          client_id: "${GOOGLE_CLIENT_ID}"
          client_secret: "${GOOGLE_CLIENT_SECRET}"
          redirect_url: "http://localhost:8080/auth/google/callback"
          scopes: ["email", "profile"]
        github:
          client_id: "${GITHUB_CLIENT_ID}"
          client_secret: "${GITHUB_CLIENT_SECRET}"
          redirect_url: "http://localhost:8080/auth/github/callback"
          scopes: ["user:email"]
    
    # LDAP Configuration
    ldap:
      host: "ldap.company.com"
      port: 389
      bind_dn: "cn=admin,dc=company,dc=com"
      bind_password: "${LDAP_PASSWORD}"
      base_dn: "ou=users,dc=company,dc=com"
      user_filter: "(uid=%s)"
      tls: true
    
    # Security Settings
    security:
      password_policy:
        min_length: 8
        require_uppercase: true
        require_lowercase: true
        require_numbers: true
        require_symbols: true
      
      mfa:
        enabled: true
        providers: ["totp", "sms"]
        backup_codes: true
      
      rate_limiting:
        enabled: true
        max_attempts: 5
        window: "15m"
        lockout_duration: "30m"
      
      session:
        secure: true
        http_only: true
        same_site: "strict"
        max_age: "24h"
    
    # RBAC Configuration
    rbac:
      enabled: true
      roles:
        admin:
          permissions: ["*"]
        user:
          permissions: ["read:profile", "update:profile"]
        guest:
          permissions: ["read:public"]
# JWT Configuration
export JWT_SECRET="your-super-secret-key"
export JWT_ISSUER="forge-app"
export JWT_EXPIRY="24h"

# OAuth2 Providers
export GOOGLE_CLIENT_ID="your-google-client-id"
export GOOGLE_CLIENT_SECRET="your-google-client-secret"
export GITHUB_CLIENT_ID="your-github-client-id"
export GITHUB_CLIENT_SECRET="your-github-client-secret"

# LDAP Configuration
export LDAP_HOST="ldap.company.com"
export LDAP_BIND_DN="cn=admin,dc=company,dc=com"
export LDAP_PASSWORD="ldap-password"

# Security Settings
export AUTH_MFA_ENABLED="true"
export AUTH_RATE_LIMITING_ENABLED="true"
export AUTH_SESSION_SECURE="true"
package main

import (
    "github.com/xraph/forge"
    "github.com/xraph/forge/extensions/auth"
)

func main() {
    app := forge.New()

    // Configure Auth extension
    authExt := auth.New(auth.Config{
        DefaultProvider: "jwt",
        
        JWT: auth.JWTConfig{
            Secret: "your-super-secret-key",
            Issuer: "forge-app",
            Audience: "forge-users",
            Expiry: time.Hour * 24,
            Algorithm: "HS256",
        },
        
        OAuth2: auth.OAuth2Config{
            Providers: map[string]auth.OAuth2Provider{
                "google": {
                    ClientID:     "your-google-client-id",
                    ClientSecret: "your-google-client-secret",
                    RedirectURL:  "http://localhost:8080/auth/google/callback",
                    Scopes:       []string{"email", "profile"},
                },
            },
        },
        
        Security: auth.SecurityConfig{
            PasswordPolicy: auth.PasswordPolicy{
                MinLength:        8,
                RequireUppercase: true,
                RequireLowercase: true,
                RequireNumbers:   true,
                RequireSymbols:   true,
            },
            MFA: auth.MFAConfig{
                Enabled:     true,
                Providers:   []string{"totp", "sms"},
                BackupCodes: true,
            },
            RateLimiting: auth.RateLimitingConfig{
                Enabled:         true,
                MaxAttempts:     5,
                Window:          time.Minute * 15,
                LockoutDuration: time.Minute * 30,
            },
        },
        
        RBAC: auth.RBACConfig{
            Enabled: true,
            Roles: map[string]auth.Role{
                "admin": {
                    Permissions: []string{"*"},
                },
                "user": {
                    Permissions: []string{"read:profile", "update:profile"},
                },
            },
        },
    })

    app.RegisterExtension(authExt)
    app.Run()
}

Usage Examples

Basic Authentication

func loginHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    
    var req struct {
        Email    string `json:"email" validate:"required,email"`
        Password string `json:"password" validate:"required"`
    }
    if err := c.Bind(&req); err != nil {
        return c.JSON(400, map[string]string{"error": "Invalid request"})
    }
    
    // Authenticate user
    user, err := auth.Authenticate(c.Context(), auth.Credentials{
        Email:    req.Email,
        Password: req.Password,
    })
    if err != nil {
        return c.JSON(401, map[string]string{"error": "Invalid credentials"})
    }
    
    // Generate JWT token
    token, err := auth.GenerateToken(c.Context(), auth.TokenClaims{
        UserID: user.ID,
        Email:  user.Email,
        Roles:  user.Roles,
    })
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to generate token"})
    }
    
    return c.JSON(200, map[string]interface{}{
        "token": token.AccessToken,
        "refresh_token": token.RefreshToken,
        "expires_in": token.ExpiresIn,
        "user": user,
    })
}
func registerHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    
    var req struct {
        Email    string `json:"email" validate:"required,email"`
        Password string `json:"password" validate:"required,min=8"`
        Name     string `json:"name" validate:"required"`
    }
    if err := c.Bind(&req); err != nil {
        return c.JSON(400, map[string]string{"error": "Invalid request"})
    }
    
    // Validate password policy
    if err := auth.ValidatePassword(req.Password); err != nil {
        return c.JSON(400, map[string]string{"error": err.Error()})
    }
    
    // Create user
    user, err := auth.CreateUser(c.Context(), auth.CreateUserRequest{
        Email:    req.Email,
        Password: req.Password,
        Name:     req.Name,
        Roles:    []string{"user"}, // Default role
    })
    if err != nil {
        if auth.IsEmailExists(err) {
            return c.JSON(409, map[string]string{"error": "Email already exists"})
        }
        return c.JSON(500, map[string]string{"error": "Failed to create user"})
    }
    
    // Send verification email
    if err := auth.SendVerificationEmail(c.Context(), user.Email); err != nil {
        // Log error but don't fail registration
        log.Printf("Failed to send verification email: %v", err)
    }
    
    return c.JSON(201, map[string]interface{}{
        "user": user,
        "message": "User created successfully. Please check your email for verification.",
    })
}
func forgotPasswordHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    
    var req struct {
        Email string `json:"email" validate:"required,email"`
    }
    if err := c.Bind(&req); err != nil {
        return c.JSON(400, map[string]string{"error": "Invalid request"})
    }
    
    // Generate reset token
    resetToken, err := auth.GeneratePasswordResetToken(c.Context(), req.Email)
    if err != nil {
        // Don't reveal if email exists
        return c.JSON(200, map[string]string{
            "message": "If the email exists, a reset link has been sent.",
        })
    }
    
    // Send reset email
    if err := auth.SendPasswordResetEmail(c.Context(), req.Email, resetToken); err != nil {
        log.Printf("Failed to send reset email: %v", err)
    }
    
    return c.JSON(200, map[string]string{
        "message": "If the email exists, a reset link has been sent.",
    })
}

func resetPasswordHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    
    var req struct {
        Token    string `json:"token" validate:"required"`
        Password string `json:"password" validate:"required,min=8"`
    }
    if err := c.Bind(&req); err != nil {
        return c.JSON(400, map[string]string{"error": "Invalid request"})
    }
    
    // Validate password policy
    if err := auth.ValidatePassword(req.Password); err != nil {
        return c.JSON(400, map[string]string{"error": err.Error()})
    }
    
    // Reset password
    if err := auth.ResetPassword(c.Context(), req.Token, req.Password); err != nil {
        return c.JSON(400, map[string]string{"error": "Invalid or expired token"})
    }
    
    return c.JSON(200, map[string]string{
        "message": "Password reset successfully",
    })
}
func logoutHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    
    // Get token from header
    token := c.Request().Header.Get("Authorization")
    if token == "" {
        return c.JSON(400, map[string]string{"error": "No token provided"})
    }
    
    // Remove "Bearer " prefix
    if len(token) > 7 && token[:7] == "Bearer " {
        token = token[7:]
    }
    
    // Blacklist token
    if err := auth.BlacklistToken(c.Context(), token); err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to logout"})
    }
    
    return c.JSON(200, map[string]string{
        "message": "Logged out successfully",
    })
}

OAuth2 Authentication

func googleLoginHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    
    // Generate OAuth2 URL
    url, state, err := auth.GetOAuth2URL(c.Context(), "google")
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to generate OAuth URL"})
    }
    
    // Store state in session for verification
    session := forge.GetSession(c)
    session.Set("oauth_state", state)
    session.Save()
    
    return c.JSON(200, map[string]string{
        "url": url,
    })
}

func googleCallbackHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    session := forge.GetSession(c)
    
    // Verify state
    storedState := session.Get("oauth_state")
    if storedState != c.Query("state") {
        return c.JSON(400, map[string]string{"error": "Invalid state"})
    }
    
    // Exchange code for token
    code := c.Query("code")
    if code == "" {
        return c.JSON(400, map[string]string{"error": "No authorization code"})
    }
    
    // Get user info from Google
    userInfo, err := auth.ExchangeOAuth2Code(c.Context(), "google", code)
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to exchange code"})
    }
    
    // Find or create user
    user, err := auth.FindOrCreateOAuth2User(c.Context(), auth.OAuth2User{
        Provider:   "google",
        ProviderID: userInfo.ID,
        Email:      userInfo.Email,
        Name:       userInfo.Name,
        Avatar:     userInfo.Picture,
    })
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to create user"})
    }
    
    // Generate JWT token
    token, err := auth.GenerateToken(c.Context(), auth.TokenClaims{
        UserID: user.ID,
        Email:  user.Email,
        Roles:  user.Roles,
    })
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to generate token"})
    }
    
    // Clear OAuth state
    session.Delete("oauth_state")
    session.Save()
    
    return c.JSON(200, map[string]interface{}{
        "token": token.AccessToken,
        "refresh_token": token.RefreshToken,
        "user": user,
    })
}
func githubLoginHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    
    url, state, err := auth.GetOAuth2URL(c.Context(), "github")
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to generate OAuth URL"})
    }
    
    session := forge.GetSession(c)
    session.Set("oauth_state", state)
    session.Save()
    
    return c.JSON(200, map[string]string{
        "url": url,
    })
}

func githubCallbackHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    session := forge.GetSession(c)
    
    // Verify state
    storedState := session.Get("oauth_state")
    if storedState != c.Query("state") {
        return c.JSON(400, map[string]string{"error": "Invalid state"})
    }
    
    code := c.Query("code")
    if code == "" {
        return c.JSON(400, map[string]string{"error": "No authorization code"})
    }
    
    // Get user info from GitHub
    userInfo, err := auth.ExchangeOAuth2Code(c.Context(), "github", code)
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to exchange code"})
    }
    
    // GitHub specific: get primary email if not public
    if userInfo.Email == "" {
        emails, err := auth.GetOAuth2UserEmails(c.Context(), "github", userInfo.AccessToken)
        if err == nil && len(emails) > 0 {
            for _, email := range emails {
                if email.Primary {
                    userInfo.Email = email.Email
                    break
                }
            }
        }
    }
    
    user, err := auth.FindOrCreateOAuth2User(c.Context(), auth.OAuth2User{
        Provider:   "github",
        ProviderID: userInfo.ID,
        Email:      userInfo.Email,
        Name:       userInfo.Name,
        Username:   userInfo.Login,
        Avatar:     userInfo.AvatarURL,
    })
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to create user"})
    }
    
    token, err := auth.GenerateToken(c.Context(), auth.TokenClaims{
        UserID: user.ID,
        Email:  user.Email,
        Roles:  user.Roles,
    })
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to generate token"})
    }
    
    session.Delete("oauth_state")
    session.Save()
    
    return c.JSON(200, map[string]interface{}{
        "token": token.AccessToken,
        "refresh_token": token.RefreshToken,
        "user": user,
    })
}
func oauthLoginHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    provider := c.Param("provider")
    
    // Validate provider
    if !auth.IsProviderSupported(provider) {
        return c.JSON(400, map[string]string{"error": "Unsupported provider"})
    }
    
    url, state, err := auth.GetOAuth2URL(c.Context(), provider)
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to generate OAuth URL"})
    }
    
    session := forge.GetSession(c)
    session.Set("oauth_state", state)
    session.Set("oauth_provider", provider)
    session.Save()
    
    return c.JSON(200, map[string]string{
        "url": url,
        "provider": provider,
    })
}

func oauthCallbackHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    session := forge.GetSession(c)
    
    provider := session.Get("oauth_provider").(string)
    storedState := session.Get("oauth_state")
    
    if storedState != c.Query("state") {
        return c.JSON(400, map[string]string{"error": "Invalid state"})
    }
    
    code := c.Query("code")
    if code == "" {
        return c.JSON(400, map[string]string{"error": "No authorization code"})
    }
    
    userInfo, err := auth.ExchangeOAuth2Code(c.Context(), provider, code)
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to exchange code"})
    }
    
    user, err := auth.FindOrCreateOAuth2User(c.Context(), auth.OAuth2User{
        Provider:   provider,
        ProviderID: userInfo.ID,
        Email:      userInfo.Email,
        Name:       userInfo.Name,
        Avatar:     userInfo.Picture,
    })
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to create user"})
    }
    
    token, err := auth.GenerateToken(c.Context(), auth.TokenClaims{
        UserID: user.ID,
        Email:  user.Email,
        Roles:  user.Roles,
    })
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to generate token"})
    }
    
    session.Delete("oauth_state")
    session.Delete("oauth_provider")
    session.Save()
    
    return c.JSON(200, map[string]interface{}{
        "token": token.AccessToken,
        "refresh_token": token.RefreshToken,
        "user": user,
        "provider": provider,
    })
}

Multi-Factor Authentication

func setupTOTPHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    userID := c.Get("user_id").(string)
    
    // Generate TOTP secret
    secret, qrCode, err := auth.GenerateTOTPSecret(c.Context(), userID)
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to generate TOTP secret"})
    }
    
    return c.JSON(200, map[string]interface{}{
        "secret": secret,
        "qr_code": qrCode,
        "manual_entry_key": secret,
    })
}

func confirmTOTPHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    userID := c.Get("user_id").(string)
    
    var req struct {
        Code string `json:"code" validate:"required,len=6"`
    }
    if err := c.Bind(&req); err != nil {
        return c.JSON(400, map[string]string{"error": "Invalid request"})
    }
    
    // Verify TOTP code
    valid, err := auth.VerifyTOTP(c.Context(), userID, req.Code)
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to verify TOTP"})
    }
    
    if !valid {
        return c.JSON(400, map[string]string{"error": "Invalid TOTP code"})
    }
    
    // Enable TOTP for user
    if err := auth.EnableTOTP(c.Context(), userID); err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to enable TOTP"})
    }
    
    // Generate backup codes
    backupCodes, err := auth.GenerateBackupCodes(c.Context(), userID)
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to generate backup codes"})
    }
    
    return c.JSON(200, map[string]interface{}{
        "message": "TOTP enabled successfully",
        "backup_codes": backupCodes,
    })
}
func verifyMFAHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    
    var req struct {
        Email    string `json:"email" validate:"required,email"`
        Password string `json:"password" validate:"required"`
        TOTPCode string `json:"totp_code"`
        BackupCode string `json:"backup_code"`
    }
    if err := c.Bind(&req); err != nil {
        return c.JSON(400, map[string]string{"error": "Invalid request"})
    }
    
    // First, authenticate with email/password
    user, err := auth.Authenticate(c.Context(), auth.Credentials{
        Email:    req.Email,
        Password: req.Password,
    })
    if err != nil {
        return c.JSON(401, map[string]string{"error": "Invalid credentials"})
    }
    
    // Check if MFA is required
    if !user.MFAEnabled {
        // Generate token directly
        token, err := auth.GenerateToken(c.Context(), auth.TokenClaims{
            UserID: user.ID,
            Email:  user.Email,
            Roles:  user.Roles,
        })
        if err != nil {
            return c.JSON(500, map[string]string{"error": "Failed to generate token"})
        }
        
        return c.JSON(200, map[string]interface{}{
            "token": token.AccessToken,
            "refresh_token": token.RefreshToken,
            "user": user,
        })
    }
    
    // Verify MFA
    var mfaValid bool
    if req.TOTPCode != "" {
        mfaValid, err = auth.VerifyTOTP(c.Context(), user.ID, req.TOTPCode)
    } else if req.BackupCode != "" {
        mfaValid, err = auth.VerifyBackupCode(c.Context(), user.ID, req.BackupCode)
    } else {
        return c.JSON(400, map[string]string{"error": "MFA code required"})
    }
    
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to verify MFA"})
    }
    
    if !mfaValid {
        return c.JSON(400, map[string]string{"error": "Invalid MFA code"})
    }
    
    // Generate token
    token, err := auth.GenerateToken(c.Context(), auth.TokenClaims{
        UserID: user.ID,
        Email:  user.Email,
        Roles:  user.Roles,
    })
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to generate token"})
    }
    
    return c.JSON(200, map[string]interface{}{
        "token": token.AccessToken,
        "refresh_token": token.RefreshToken,
        "user": user,
    })
}
func sendSMSCodeHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    userID := c.Get("user_id").(string)
    
    // Get user's phone number
    user, err := auth.GetUser(c.Context(), userID)
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to get user"})
    }
    
    if user.PhoneNumber == "" {
        return c.JSON(400, map[string]string{"error": "No phone number registered"})
    }
    
    // Generate and send SMS code
    code, err := auth.SendSMSCode(c.Context(), user.PhoneNumber)
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to send SMS code"})
    }
    
    return c.JSON(200, map[string]interface{}{
        "message": "SMS code sent",
        "phone_number": maskPhoneNumber(user.PhoneNumber),
    })
}

func verifySMSCodeHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    userID := c.Get("user_id").(string)
    
    var req struct {
        Code string `json:"code" validate:"required,len=6"`
    }
    if err := c.Bind(&req); err != nil {
        return c.JSON(400, map[string]string{"error": "Invalid request"})
    }
    
    // Verify SMS code
    valid, err := auth.VerifySMSCode(c.Context(), userID, req.Code)
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to verify SMS code"})
    }
    
    if !valid {
        return c.JSON(400, map[string]string{"error": "Invalid SMS code"})
    }
    
    return c.JSON(200, map[string]string{
        "message": "SMS code verified successfully",
    })
}

func maskPhoneNumber(phone string) string {
    if len(phone) < 4 {
        return phone
    }
    return phone[:len(phone)-4] + "****"
}
func generateBackupCodesHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    userID := c.Get("user_id").(string)
    
    // Verify current password for security
    var req struct {
        Password string `json:"password" validate:"required"`
    }
    if err := c.Bind(&req); err != nil {
        return c.JSON(400, map[string]string{"error": "Invalid request"})
    }
    
    // Get user and verify password
    user, err := auth.GetUser(c.Context(), userID)
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to get user"})
    }
    
    if !auth.VerifyPassword(req.Password, user.PasswordHash) {
        return c.JSON(401, map[string]string{"error": "Invalid password"})
    }
    
    // Generate new backup codes
    backupCodes, err := auth.GenerateBackupCodes(c.Context(), userID)
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to generate backup codes"})
    }
    
    return c.JSON(200, map[string]interface{}{
        "backup_codes": backupCodes,
        "message": "New backup codes generated. Store them securely.",
    })
}

func viewBackupCodesHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    userID := c.Get("user_id").(string)
    
    // Get remaining backup codes
    codes, err := auth.GetBackupCodes(c.Context(), userID)
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to get backup codes"})
    }
    
    // Mask codes for security (show only first 2 characters)
    maskedCodes := make([]string, len(codes))
    for i, code := range codes {
        if len(code) > 2 {
            maskedCodes[i] = code[:2] + "******"
        } else {
            maskedCodes[i] = "******"
        }
    }
    
    return c.JSON(200, map[string]interface{}{
        "backup_codes": maskedCodes,
        "count": len(codes),
    })
}

Middleware and Authorization

func setupAuthMiddleware(app *forge.App) {
    auth := forge.GetAuth(app)
    
    // JWT Authentication middleware
    jwtMiddleware := auth.JWTMiddleware(auth.JWTMiddlewareConfig{
        SkipPaths: []string{
            "/auth/login",
            "/auth/register",
            "/auth/forgot-password",
            "/health",
            "/metrics",
        },
        ErrorHandler: func(c forge.Context, err error) error {
            return c.JSON(401, map[string]string{
                "error": "Unauthorized",
                "message": err.Error(),
            })
        },
    })
    
    // Apply middleware to protected routes
    protected := app.Group("/api", jwtMiddleware)
    
    // User routes
    protected.GET("/profile", getProfileHandler)
    protected.PUT("/profile", updateProfileHandler)
    
    // Admin routes with role check
    admin := protected.Group("/admin", auth.RequireRole("admin"))
    admin.GET("/users", listUsersHandler)
    admin.DELETE("/users/:id", deleteUserHandler)
}

func getProfileHandler(c forge.Context) error {
    // User is already authenticated by middleware
    userID := c.Get("user_id").(string)
    
    auth := forge.GetAuth(c)
    user, err := auth.GetUser(c.Context(), userID)
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to get user"})
    }
    
    return c.JSON(200, user)
}
func setupRoleBasedRoutes(app *forge.App) {
    auth := forge.GetAuth(app)
    
    // Different access levels
    api := app.Group("/api", auth.JWTMiddleware())
    
    // User level access
    user := api.Group("/user", auth.RequireRole("user"))
    user.GET("/profile", getUserProfileHandler)
    user.PUT("/profile", updateUserProfileHandler)
    
    // Manager level access
    manager := api.Group("/manager", auth.RequireRole("manager"))
    manager.GET("/team", getTeamHandler)
    manager.POST("/team/invite", inviteTeamMemberHandler)
    
    // Admin level access
    admin := api.Group("/admin", auth.RequireRole("admin"))
    admin.GET("/users", listAllUsersHandler)
    admin.DELETE("/users/:id", deleteUserHandler)
    admin.POST("/roles", createRoleHandler)
    
    // Super admin access
    superAdmin := api.Group("/super-admin", auth.RequireRole("super_admin"))
    superAdmin.GET("/system", getSystemInfoHandler)
    superAdmin.POST("/maintenance", enableMaintenanceModeHandler)
}

func createRoleHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    
    var req struct {
        Name        string   `json:"name" validate:"required"`
        Description string   `json:"description"`
        Permissions []string `json:"permissions" validate:"required"`
    }
    if err := c.Bind(&req); err != nil {
        return c.JSON(400, map[string]string{"error": "Invalid request"})
    }
    
    role, err := auth.CreateRole(c.Context(), auth.Role{
        Name:        req.Name,
        Description: req.Description,
        Permissions: req.Permissions,
    })
    if err != nil {
        return c.JSON(500, map[string]string{"error": "Failed to create role"})
    }
    
    return c.JSON(201, role)
}
func setupPermissionBasedRoutes(app *forge.App) {
    auth := forge.GetAuth(app)
    
    api := app.Group("/api", auth.JWTMiddleware())
    
    // Resource-specific permissions
    api.GET("/posts", auth.RequirePermission("read:posts"), listPostsHandler)
    api.POST("/posts", auth.RequirePermission("create:posts"), createPostHandler)
    api.PUT("/posts/:id", auth.RequirePermission("update:posts"), updatePostHandler)
    api.DELETE("/posts/:id", auth.RequirePermission("delete:posts"), deletePostHandler)
    
    // Multiple permissions (user needs ALL)
    api.GET("/admin/reports", 
        auth.RequirePermissions([]string{"read:reports", "access:admin"}),
        getReportsHandler)
    
    // Any permission (user needs ANY)
    api.GET("/content", 
        auth.RequireAnyPermission([]string{"read:posts", "read:pages"}),
        getContentHandler)
}

func updatePostHandler(c forge.Context) error {
    auth := forge.GetAuth(c)
    userID := c.Get("user_id").(string)
    postID := c.Param("id")
    
    // Check if user owns the post or has admin permission
    post, err := getPost(postID)
    if err != nil {
        return c.JSON(404, map[string]string{"error": "Post not found"})
    }
    
    if post.AuthorID != userID {
        // Check if user has admin permission to edit any post
        hasPermission, err := auth.HasPermission(c.Context(), userID, "admin:posts")
        if err != nil || !hasPermission {
            return c.JSON(403, map[string]string{"error": "Insufficient permissions"})
        }
    }
    
    // Update post logic here
    return c.JSON(200, map[string]string{"message": "Post updated"})
}

func customPermissionMiddleware(permission string) forge.MiddlewareFunc {
    return func(next forge.HandlerFunc) forge.HandlerFunc {
        return func(c forge.Context) error {
            auth := forge.GetAuth(c)
            userID := c.Get("user_id").(string)
            
            hasPermission, err := auth.HasPermission(c.Context(), userID, permission)
            if err != nil {
                return c.JSON(500, map[string]string{"error": "Permission check failed"})
            }
            
            if !hasPermission {
                return c.JSON(403, map[string]string{"error": "Insufficient permissions"})
            }
            
            return next(c)
        }
    }
}
func setupRateLimiting(app *forge.App) {
    auth := forge.GetAuth(app)
    
    // Global rate limiting
    app.Use(auth.RateLimitMiddleware(auth.RateLimitConfig{
        MaxRequests: 100,
        Window:      time.Minute,
        KeyFunc: func(c forge.Context) string {
            // Rate limit by IP
            return c.RealIP()
        },
    }))
    
    // Authentication specific rate limiting
    authGroup := app.Group("/auth")
    
    // Stricter rate limiting for login attempts
    authGroup.Use(auth.RateLimitMiddleware(auth.RateLimitConfig{
        MaxRequests: 5,
        Window:      time.Minute * 15,
        KeyFunc: func(c forge.Context) string {
            // Rate limit by IP for auth endpoints
            return "auth:" + c.RealIP()
        },
        OnLimitReached: func(c forge.Context) error {
            return c.JSON(429, map[string]interface{}{
                "error": "Too many authentication attempts",
                "retry_after": 900, // 15 minutes
            })
        },
    }))
    
    authGroup.POST("/login", loginHandler)
    authGroup.POST("/register", registerHandler)
    authGroup.POST("/forgot-password", forgotPasswordHandler)
    
    // User-specific rate limiting for authenticated routes
    api := app.Group("/api", auth.JWTMiddleware())
    api.Use(auth.RateLimitMiddleware(auth.RateLimitConfig{
        MaxRequests: 1000,
        Window:      time.Hour,
        KeyFunc: func(c forge.Context) string {
            // Rate limit by user ID
            userID := c.Get("user_id").(string)
            return "user:" + userID
        },
    }))
}

func setupAdvancedRateLimiting(app *forge.App) {
    auth := forge.GetAuth(app)
    
    // Different limits for different user roles
    api := app.Group("/api", auth.JWTMiddleware())
    
    api.Use(func(next forge.HandlerFunc) forge.HandlerFunc {
        return func(c forge.Context) error {
            userID := c.Get("user_id").(string)
            user, err := auth.GetUser(c.Context(), userID)
            if err != nil {
                return next(c)
            }
            
            var maxRequests int
            var window time.Duration
            
            // Set limits based on user role
            switch {
            case contains(user.Roles, "premium"):
                maxRequests = 10000
                window = time.Hour
            case contains(user.Roles, "admin"):
                maxRequests = 50000
                window = time.Hour
            default:
                maxRequests = 1000
                window = time.Hour
            }
            
            // Apply rate limiting
            rateLimiter := auth.RateLimitMiddleware(auth.RateLimitConfig{
                MaxRequests: maxRequests,
                Window:      window,
                KeyFunc: func(c forge.Context) string {
                    return "user:" + userID
                },
            })
            
            return rateLimiter(next)(c)
        }
    })
}

func contains(slice []string, item string) bool {
    for _, s := range slice {
        if s == item {
            return true
        }
    }
    return false
}

Advanced Features

Custom Authentication Provider

// Implement custom authentication provider
type CustomAuthProvider struct {
    config CustomAuthConfig
}

func (p *CustomAuthProvider) Name() string {
    return "custom"
}

func (p *CustomAuthProvider) Type() auth.SecuritySchemeType {
    return auth.SecuritySchemeTypeAPIKey
}

func (p *CustomAuthProvider) Authenticate(ctx context.Context, credentials auth.Credentials) (*auth.User, error) {
    // Custom authentication logic
    apiKey := credentials.APIKey
    
    // Validate API key against your system
    user, err := p.validateAPIKey(ctx, apiKey)
    if err != nil {
        return nil, auth.ErrInvalidCredentials
    }
    
    return user, nil
}

func (p *CustomAuthProvider) Middleware() forge.MiddlewareFunc {
    return func(next forge.HandlerFunc) forge.HandlerFunc {
        return func(c forge.Context) error {
            apiKey := c.Request().Header.Get("X-API-Key")
            if apiKey == "" {
                return c.JSON(401, map[string]string{"error": "API key required"})
            }
            
            user, err := p.Authenticate(c.Context(), auth.Credentials{APIKey: apiKey})
            if err != nil {
                return c.JSON(401, map[string]string{"error": "Invalid API key"})
            }
            
            c.Set("user", user)
            c.Set("user_id", user.ID)
            
            return next(c)
        }
    }
}

// Register custom provider
func setupCustomAuth(app *forge.App) {
    auth := forge.GetAuth(app)
    
    customProvider := &CustomAuthProvider{
        config: CustomAuthConfig{
            // Your custom config
        },
    }
    
    auth.RegisterProvider(customProvider)
}

Session Management

func setupSessionManagement(app *forge.App) {
    auth := forge.GetAuth(app)
    
    // Session middleware
    app.Use(auth.SessionMiddleware(auth.SessionConfig{
        Store:    "redis", // or "memory", "database"
        Secure:   true,
        HTTPOnly: true,
        SameSite: "strict",
        MaxAge:   time.Hour * 24,
    }))
    
    // Login with session
    app.POST("/auth/session/login", func(c forge.Context) error {
        var req struct {
            Email    string `json:"email"`
            Password string `json:"password"`
        }
        if err := c.Bind(&req); err != nil {
            return c.JSON(400, map[string]string{"error": "Invalid request"})
        }
        
        user, err := auth.Authenticate(c.Context(), auth.Credentials{
            Email:    req.Email,
            Password: req.Password,
        })
        if err != nil {
            return c.JSON(401, map[string]string{"error": "Invalid credentials"})
        }
        
        // Create session
        session := forge.GetSession(c)
        session.Set("user_id", user.ID)
        session.Set("authenticated", true)
        session.Save()
        
        return c.JSON(200, map[string]interface{}{
            "message": "Logged in successfully",
            "user": user,
        })
    })
    
    // Session-based authentication middleware
    sessionAuth := func(next forge.HandlerFunc) forge.HandlerFunc {
        return func(c forge.Context) error {
            session := forge.GetSession(c)
            
            userID := session.Get("user_id")
            authenticated := session.Get("authenticated")
            
            if userID == nil || authenticated != true {
                return c.JSON(401, map[string]string{"error": "Not authenticated"})
            }
            
            c.Set("user_id", userID)
            return next(c)
        }
    }
    
    // Protected routes with session auth
    protected := app.Group("/api", sessionAuth)
    protected.GET("/profile", getProfileHandler)
}

Best Practices

Security

  • Always use HTTPS in production
  • Store secrets securely (environment variables, secret managers)
  • Implement proper password policies
  • Use secure session configurations
  • Enable MFA for sensitive accounts
  • Regularly rotate secrets and tokens

Performance

  • Use caching for user data and permissions
  • Implement efficient token validation
  • Use connection pooling for external providers
  • Monitor authentication performance

Error Handling

  • Don't reveal sensitive information in error messages
  • Log authentication events for security monitoring
  • Implement proper rate limiting
  • Handle edge cases gracefully

Monitoring

  • Track authentication metrics
  • Monitor failed login attempts
  • Set up alerts for suspicious activity
  • Audit user permissions regularly

Troubleshooting

Common Issues

JWT Token Issues

// Validate JWT configuration
config := auth.GetJWTConfig()
if config.Secret == "" {
    log.Fatal("JWT secret not configured")
}

// Check token expiry
token, err := auth.ParseToken(tokenString)
if err != nil {
    if auth.IsTokenExpired(err) {
        // Handle expired token
        return refreshToken(c)
    }
    return c.JSON(401, map[string]string{"error": "Invalid token"})
}

OAuth2 Configuration

// Verify OAuth2 provider setup
providers := auth.GetOAuth2Providers()
for name, provider := range providers {
    if provider.ClientID == "" || provider.ClientSecret == "" {
        log.Printf("OAuth2 provider %s not properly configured", name)
    }
}

Rate Limiting Issues

// Check rate limit status
status, err := auth.GetRateLimitStatus(c.Context(), "user:123")
if err != nil {
    log.Printf("Rate limit check failed: %v", err)
}

log.Printf("Rate limit: %d/%d requests, resets at %v", 
    status.Current, status.Limit, status.ResetTime)

Always test authentication flows thoroughly in a staging environment before deploying to production.

Next Steps

Setup: Configure your authentication providers and security settings

Integration: Implement authentication in your application routes

Security: Enable MFA and implement proper security measures

Monitoring: Set up authentication monitoring and alerting

Optimization: Optimize performance and implement caching

How is this guide?

Last updated on