---
globs: **/routes/**,**/auth/**,**/login/**,**/*auth*,**/*security*
description: Complete authentication, security, and debugging patterns for React + Go POS system
---

# 🔐 Authentication & Security Patterns

## 🚀 Essential Authentication Architecture

### JWT-Based Authentication Flow
```typescript
// Complete authentication workflow
class APIClient {
  constructor() {
    const apiUrl = import.meta.env?.VITE_API_URL || 'http://localhost:8080/api/v1';
    console.log('🔧 API Client baseURL:', apiUrl);
    
    this.client = axios.create({
      baseURL: apiUrl,
      timeout: 30000,
      headers: { 'Content-Type': 'application/json' }
    });

    // Auto-attach token from localStorage
    this.loadStoredAuth();
  }

  private loadStoredAuth(): void {
    const token = localStorage.getItem('pos_token');
    if (token) {
      this.client.defaults.headers.common['Authorization'] = `Bearer ${token}`;
    }
  }

  setAuthToken(token: string): void {
    localStorage.setItem('pos_token', token);
    this.client.defaults.headers.common['Authorization'] = `Bearer ${token}`;
  }

  clearAuth(): void {
    localStorage.removeItem('pos_token');
    localStorage.removeItem('pos_user');
    delete this.client.defaults.headers.common['Authorization'];
  }

  isAuthenticated(): boolean {
    return !!localStorage.getItem('pos_token');
  }
}
```

## 🏗️ React Authentication Components

### Protected Route Pattern (Avoid Infinite Redirects)
```typescript
function HomePage() {
  // ✅ ALL HOOKS AT TOP LEVEL - NEVER after returns
  const [user, setUser] = useState<User | null>(null);
  const [isLoadingAuth, setIsLoadingAuth] = useState(true); // Critical: Start true

  const { isLoading: isVerifying, error } = useQuery({
    queryKey: ['currentUser'],
    queryFn: () => apiClient.getCurrentUser(),
    enabled: false, // Control when to verify
    retry: 1,
  });

  // Load auth state from localStorage FIRST
  useEffect(() => {
    const loadAuthState = async () => {
      const token = localStorage.getItem('pos_token');
      const storedUser = localStorage.getItem('pos_user');
      
      console.log('🔍 Loading auth - token:', token ? 'exists' : 'missing');
      console.log('🔍 Loading auth - user:', storedUser ? 'exists' : 'missing');
      
      if (storedUser && token) {
        try {
          const parsedUser = JSON.parse(storedUser);
          setUser(parsedUser);
          console.log('✅ Auth loaded - user role:', parsedUser.role);
        } catch (error) {
          console.error('❌ Invalid stored auth data, clearing');
          apiClient.clearAuth();
        }
      }
      
      setIsLoadingAuth(false);
    };
    
    loadAuthState();
  }, []);

  // ✅ CRITICAL: Wait for localStorage loading before auth checks
  if (isLoadingAuth) {
    return <LoadingSpinner message="Loading authentication..." />;
  }

  // Only check auth AFTER loading is complete
  if (!apiClient.isAuthenticated() || !user) {
    console.log('🔄 Not authenticated, redirecting to login');
    return <Navigate to="/login" replace />;
  }

  // Render protected content with user context
  return <RoleBasedLayout user={user} />;
}
```

### Login Component Pattern
```typescript
function LoginPage() {
  const [error, setError] = useState<string | null>(null);
  
  // Redirect if already authenticated
  if (apiClient.isAuthenticated()) {
    return <Navigate to="/" replace />;
  }

  const loginMutation = useMutation({
    mutationFn: async (credentials: LoginRequest) => {
      console.log('🔄 Attempting login...');
      return await apiClient.login(credentials);
    },
    onSuccess: (data) => {
      console.log('✅ Login success:', data.success);
      
      if (data.success && data.data) {
        // Set auth token first
        apiClient.setAuthToken(data.data.token);
        
        // Store user data
        localStorage.setItem('pos_user', JSON.stringify(data.data.user));
        
        console.log('✅ Auth stored - role:', data.data.user.role);
        
        // Brief delay prevents race conditions
        setTimeout(() => {
          router.navigate({ to: '/' });
        }, 100);
      }
    },
    onError: (error: any) => {
      console.error('❌ Login failed:', error.message);
      setError(error.message || 'Login failed');
    },
  });

  return (
    <LoginForm 
      onSubmit={loginMutation.mutate}
      isLoading={loginMutation.isPending}
      error={error}
    />
  );
}
```

## 🛡️ Backend Security Patterns

### Go JWT Middleware
```go
// JWT authentication middleware
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        tokenString := c.GetHeader("Authorization")
        if tokenString == "" {
            c.JSON(http.StatusUnauthorized, models.APIResponse{
                Success: false,
                Message: "Authorization header required",
                Error:   stringPtr("missing_auth_header"),
            })
            c.Abort()
            return
        }

        // Remove "Bearer " prefix
        if len(tokenString) > 7 && tokenString[:7] == "Bearer " {
            tokenString = tokenString[7:]
        }

        token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, fmt.Errorf("unexpected signing method")
            }
            return []byte(os.Getenv("JWT_SECRET")), nil
        })

        if err != nil || !token.Valid {
            c.JSON(http.StatusUnauthorized, models.APIResponse{
                Success: false,
                Message: "Invalid or expired token",
                Error:   stringPtr("invalid_token"),
            })
            c.Abort()
            return
        }

        if claims, ok := token.Claims.(jwt.MapClaims); ok {
            c.Set("user_id", claims["user_id"])
            c.Set("username", claims["username"])
            c.Set("role", claims["role"])
        }

        c.Next()
    }
}

// Role-based access control
func RequireRoles(allowedRoles ...string) gin.HandlerFunc {
    return func(c *gin.Context) {
        userRole, exists := c.Get("role")
        if !exists {
            c.JSON(http.StatusForbidden, models.APIResponse{
                Success: false,
                Message: "Role information not found",
                Error:   stringPtr("missing_role"),
            })
            c.Abort()
            return
        }

        role := userRole.(string)
        for _, allowedRole := range allowedRoles {
            if role == allowedRole {
                c.Next()
                return
            }
        }

        c.JSON(http.StatusForbidden, models.APIResponse{
            Success: false,
            Message: "Insufficient permissions",
            Error:   stringPtr("insufficient_permissions"),
        })
        c.Abort()
    }
}
```

### CORS Configuration for Development
```go
// Dynamic port CORS for development flexibility
func setupCORS(router *gin.Engine) {
    config := cors.New(cors.Config{
        AllowOrigins: []string{
            "http://localhost:3000",
            "http://localhost:3001", 
            "http://localhost:3002",
            "http://localhost:3003",
            "http://localhost:5173", // Vite default
        },
        AllowMethods: []string{
            "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS",
        },
        AllowHeaders: []string{
            "Origin", "Content-Type", "Content-Length", 
            "Accept-Encoding", "X-CSRF-Token", "Authorization",
            "X-Requested-With",
        },
        AllowCredentials: true,
        MaxAge:           12 * time.Hour,
    })
    
    router.Use(config)
}
```

## 🐛 Common Issues & Solutions

### 1. Infinite Redirect Loop
**Symptoms:** Continuous redirects between `/login` and `/`

**Root Cause:**
```typescript
// ❌ WRONG - Auth check before localStorage loading
if (!apiClient.isAuthenticated() || !user) {
  return <Navigate to="/login" />; // Creates loop
}
```

**Solution:**
```typescript
// ✅ CORRECT - Wait for loading state
if (isLoadingAuth) {
  return <LoadingSpinner />;
}

if (!apiClient.isAuthenticated() || !user) {
  return <Navigate to="/login" replace />;
}
```

### 2. React Hooks Rules Violation
**Symptoms:** "Rendered more hooks than during the previous render"

**Solution:**
```typescript
// ✅ CORRECT - All hooks at top level
function Component() {
  const { data } = useQuery(...); // Hook at top
  
  if (someCondition) {
    return <div>Early return</div>; // Return after hooks
  }
}
```

### 3. CORS Debugging
**Test CORS Configuration:**
```bash
# Test preflight request
curl -s -H "Origin: http://localhost:3001" \
     -X OPTIONS http://localhost:8080/api/v1/auth/login -I

# Expected headers:
# Access-Control-Allow-Origin: http://localhost:3001
# Access-Control-Allow-Methods: GET,POST,PUT,DELETE,PATCH,OPTIONS
```

**Quick CORS Fix:**
```bash
# Add new port to CORS (adjust port as needed)
# Edit backend/main.go CORS configuration
# Restart backend container
docker compose restart backend
```

### 4. Environment Variables
**Common Issue:** API requests to wrong URLs

**Solution:**
```bash
# Set environment variables in both locations
echo "VITE_API_URL=http://localhost:8080/api/v1" > .env
echo "VITE_API_URL=http://localhost:8080/api/v1" > frontend/.env

# Recreate containers (restart isn't enough for env vars)
make down
make dev
```

## 🔍 Authentication Debugging Checklist

### Frontend Issues
- [ ] Check browser localStorage: `pos_token` and `pos_user`
- [ ] Verify API client baseURL in console logs
- [ ] Check Network tab for 401/404 vs CORS errors
- [ ] Confirm hooks are at component top level
- [ ] Test with cleared localStorage

### Backend Issues
- [ ] Check backend logs: `docker logs pos-backend-dev --tail 50`
- [ ] Verify JWT_SECRET environment variable
- [ ] Test API endpoints with curl
- [ ] Confirm CORS includes frontend port
- [ ] Check route registration patterns

### CORS Issues
- [ ] Check current frontend port in terminal
- [ ] Test CORS preflight with curl
- [ ] Verify backend CORS allows current port
- [ ] Restart backend after CORS changes
- [ ] Clear browser cache/storage

## 🏛️ Security Best Practices

### Password Security
```go
import "golang.org/x/crypto/bcrypt"

func HashPassword(password string) (string, error) {
    bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
    return string(bytes), err
}

func VerifyPassword(hashedPassword, password string) error {
    return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
}
```

### Input Validation
```go
// Validate UUIDs
orderID, err := uuid.Parse(c.Param("id"))
if err != nil {
    c.JSON(http.StatusBadRequest, models.APIResponse{
        Success: false,
        Message: "Invalid order ID format",
        Error:   stringPtr("invalid_uuid"),
    })
    return
}

// Validate enums
validStatuses := []string{"pending", "confirmed", "preparing", "ready", "served", "completed", "cancelled"}
if !contains(validStatuses, req.Status) {
    c.JSON(http.StatusBadRequest, models.APIResponse{
        Success: false,
        Message: "Invalid order status",
        Error:   stringPtr("invalid_status"),
    })
    return
}
```

### Security Logging
```go
// Log security events
log.Printf("LOGIN_SUCCESS: user=%s, ip=%s, role=%s", username, c.ClientIP(), user.Role)
log.Printf("LOGIN_FAILED: username=%s, ip=%s", username, c.ClientIP())
log.Printf("ACCESS_DENIED: user=%s, role=%s, endpoint=%s", username, role, c.Request.URL.Path)
```

## 🚀 Development Workflow

### Environment Setup Commands
```bash
# Essential development commands
make dev          # Start with proper environment variables
make logs         # Monitor authentication flow
make status       # Check system health

# Authentication debugging
make logs-backend # Check JWT processing
make logs-frontend# Check client-side auth

# Reset authentication state
make db-reset     # Fresh database with demo users
make clean        # Clear all Docker state
```

### Demo Accounts for Testing
| Role | Username | Password | Permissions |
|------|----------|----------|-------------|
| **👑 Admin** | `admin` | `admin123` | Full system access |
| **🍽️ Server** | `server1` | `server123` | Dine-in orders only |
| **💰 Counter** | `counter1` | `counter123` | All orders + payments |
| **👨‍🍳 Kitchen** | `kitchen1` | `kitchen123` | Order preparation |

### Quick Authentication Test
```typescript
// Test authentication in browser console
console.log('Token:', localStorage.getItem('pos_token'));
console.log('User:', JSON.parse(localStorage.getItem('pos_user') || 'null'));
console.log('API authenticated:', apiClient.isAuthenticated());

// Clear auth for testing
apiClient.clearAuth();
```