Cursor rule
.cursor/rules/authentication-and-security-patterns.mdcComplete authentication, security, and debugging patterns for React + Go POS system
Cursor rules
Quality
81/100
Scores the file, not the repository.Length
1,332 words
38 headings · 16 code blocksRepository
118
— · pushed 339 days agoLast changed
3 days ago
First indexed 3 days ago.123456# 🔐 Authentication & Security Patterns78## 🚀 Essential Authentication Architecture910### JWT-Based Authentication Flow11```typescript12// Complete authentication workflow13class APIClient {14 constructor() {15 const apiUrl = import.meta.env?.VITE_API_URL || 'http://localhost:8080/api/v1';16 console.log('🔧 API Client baseURL:', apiUrl);1718 this.client = axios.create({19 baseURL: apiUrl,20 timeout: 30000,21 headers: { 'Content-Type': 'application/json' }22 });2324 // Auto-attach token from localStorage25 this.loadStoredAuth();26 }2728 private loadStoredAuth(): void {29 const token = localStorage.getItem('pos_token');30 if (token) {31 this.client.defaults.headers.common['Authorization'] = `Bearer ${token}`;32 }33 }3435 setAuthToken(token: string): void {36 localStorage.setItem('pos_token', token);37 this.client.defaults.headers.common['Authorization'] = `Bearer ${token}`;38 }3940 clearAuth(): void {41 localStorage.removeItem('pos_token');42 localStorage.removeItem('pos_user');43 delete this.client.defaults.headers.common['Authorization'];44 }4546 isAuthenticated(): boolean {47 return !!localStorage.getItem('pos_token');48 }49}50```5152## 🏗️ React Authentication Components5354### Protected Route Pattern (Avoid Infinite Redirects)55```typescript56function HomePage() {57 // ✅ ALL HOOKS AT TOP LEVEL - NEVER after returns58 const [user, setUser] = useState<User | null>(null);59 const [isLoadingAuth, setIsLoadingAuth] = useState(true); // Critical: Start true6061 const { isLoading: isVerifying, error } = useQuery({62 queryKey: ['currentUser'],63 queryFn: () => apiClient.getCurrentUser(),64 enabled: false, // Control when to verify65 retry: 1,66 });6768 // Load auth state from localStorage FIRST69 useEffect(() => {70 const loadAuthState = async () => {71 const token = localStorage.getItem('pos_token');72 const storedUser = localStorage.getItem('pos_user');7374 console.log('🔍 Loading auth - token:', token ? 'exists' : 'missing');75 console.log('🔍 Loading auth - user:', storedUser ? 'exists' : 'missing');7677 if (storedUser && token) {78 try {79 const parsedUser = JSON.parse(storedUser);80 setUser(parsedUser);81 console.log('✅ Auth loaded - user role:', parsedUser.role);82 } catch (error) {83 console.error('❌ Invalid stored auth data, clearing');84 apiClient.clearAuth();85 }86 }8788 setIsLoadingAuth(false);89 };9091 loadAuthState();92 }, []);9394 // ✅ CRITICAL: Wait for localStorage loading before auth checks95 if (isLoadingAuth) {96 return <LoadingSpinner message="Loading authentication..." />;97 }9899 // Only check auth AFTER loading is complete100 if (!apiClient.isAuthenticated() || !user) {101 console.log('🔄 Not authenticated, redirecting to login');102 return <Navigate to="/login" replace />;103 }104105 // Render protected content with user context106 return <RoleBasedLayout user={user} />;107}108```109110### Login Component Pattern111```typescript112function LoginPage() {113 const [error, setError] = useState<string | null>(null);114115 // Redirect if already authenticated116 if (apiClient.isAuthenticated()) {117 return <Navigate to="/" replace />;118 }119120 const loginMutation = useMutation({121 mutationFn: async (credentials: LoginRequest) => {122 console.log('🔄 Attempting login...');123 return await apiClient.login(credentials);124 },125 onSuccess: (data) => {126 console.log('✅ Login success:', data.success);127128 if (data.success && data.data) {129 // Set auth token first130 apiClient.setAuthToken(data.data.token);131132 // Store user data133 localStorage.setItem('pos_user', JSON.stringify(data.data.user));134135 console.log('✅ Auth stored - role:', data.data.user.role);136137 // Brief delay prevents race conditions138 setTimeout(() => {139 router.navigate({ to: '/' });140 }, 100);141 }142 },143 onError: (error: any) => {144 console.error('❌ Login failed:', error.message);145 setError(error.message || 'Login failed');146 },147 });148149 return (150 <LoginForm151 onSubmit={loginMutation.mutate}152 isLoading={loginMutation.isPending}153 error={error}154 />155 );156}157```158159## 🛡️ Backend Security Patterns160161### Go JWT Middleware162```go163// JWT authentication middleware164func AuthMiddleware() gin.HandlerFunc {165 return func(c *gin.Context) {166 tokenString := c.GetHeader("Authorization")167 if tokenString == "" {168 c.JSON(http.StatusUnauthorized, models.APIResponse{169 Success: false,170 Message: "Authorization header required",171 Error: stringPtr("missing_auth_header"),172 })173 c.Abort()174 return175 }176177 // Remove "Bearer " prefix178 if len(tokenString) > 7 && tokenString[:7] == "Bearer " {179 tokenString = tokenString[7:]180 }181182 token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {183 if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {184 return nil, fmt.Errorf("unexpected signing method")185 }186 return []byte(os.Getenv("JWT_SECRET")), nil187 })188189 if err != nil || !token.Valid {190 c.JSON(http.StatusUnauthorized, models.APIResponse{191 Success: false,192 Message: "Invalid or expired token",193 Error: stringPtr("invalid_token"),194 })195 c.Abort()196 return197 }198199 if claims, ok := token.Claims.(jwt.MapClaims); ok {200 c.Set("user_id", claims["user_id"])201 c.Set("username", claims["username"])202 c.Set("role", claims["role"])203 }204205 c.Next()206 }207}208209// Role-based access control210func RequireRoles(allowedRoles ...string) gin.HandlerFunc {211 return func(c *gin.Context) {212 userRole, exists := c.Get("role")213 if !exists {214 c.JSON(http.StatusForbidden, models.APIResponse{215 Success: false,216 Message: "Role information not found",217 Error: stringPtr("missing_role"),218 })219 c.Abort()220 return221 }222223 role := userRole.(string)224 for _, allowedRole := range allowedRoles {225 if role == allowedRole {226 c.Next()227 return228 }229 }230231 c.JSON(http.StatusForbidden, models.APIResponse{232 Success: false,233 Message: "Insufficient permissions",234 Error: stringPtr("insufficient_permissions"),235 })236 c.Abort()237 }238}239```240241### CORS Configuration for Development242```go243// Dynamic port CORS for development flexibility244func setupCORS(router *gin.Engine) {245 config := cors.New(cors.Config{246 AllowOrigins: []string{247 "http://localhost:3000",248 "http://localhost:3001",249 "http://localhost:3002",250 "http://localhost:3003",251 "http://localhost:5173", // Vite default252 },253 AllowMethods: []string{254 "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS",255 },256 AllowHeaders: []string{257 "Origin", "Content-Type", "Content-Length",258 "Accept-Encoding", "X-CSRF-Token", "Authorization",259 "X-Requested-With",260 },261 AllowCredentials: true,262 MaxAge: 12 * time.Hour,263 })264265 router.Use(config)266}267```268269## 🐛 Common Issues & Solutions270271### 1. Infinite Redirect Loop272**Symptoms:** Continuous redirects between `/login` and `/`273274**Root Cause:**275```typescript276// ❌ WRONG - Auth check before localStorage loading277if (!apiClient.isAuthenticated() || !user) {278 return <Navigate to="/login" />; // Creates loop279}280```281282**Solution:**283```typescript284// ✅ CORRECT - Wait for loading state285if (isLoadingAuth) {286 return <LoadingSpinner />;287}288289if (!apiClient.isAuthenticated() || !user) {290 return <Navigate to="/login" replace />;291}292```293294### 2. React Hooks Rules Violation295**Symptoms:** "Rendered more hooks than during the previous render"296297**Solution:**298```typescript299// ✅ CORRECT - All hooks at top level300function Component() {301 const { data } = useQuery(...); // Hook at top302303 if (someCondition) {304 return <div>Early return</div>; // Return after hooks305 }306}307```308309### 3. CORS Debugging310**Test CORS Configuration:**311```bash312# Test preflight request313curl -s -H "Origin: http://localhost:3001" \314 -X OPTIONS http://localhost:8080/api/v1/auth/login -I315316# Expected headers:317# Access-Control-Allow-Origin: http://localhost:3001318# Access-Control-Allow-Methods: GET,POST,PUT,DELETE,PATCH,OPTIONS319```320321**Quick CORS Fix:**322```bash323# Add new port to CORS (adjust port as needed)324# Edit backend/main.go CORS configuration325# Restart backend container326docker compose restart backend327```328329### 4. Environment Variables330**Common Issue:** API requests to wrong URLs331332**Solution:**333```bash334# Set environment variables in both locations335echo "VITE_API_URL=http://localhost:8080/api/v1" > .env336echo "VITE_API_URL=http://localhost:8080/api/v1" > frontend/.env337338# Recreate containers (restart isn't enough for env vars)339make down340make dev341```342343## 🔍 Authentication Debugging Checklist344345### Frontend Issues346- [ ] Check browser localStorage: `pos_token` and `pos_user`347- [ ] Verify API client baseURL in console logs348- [ ] Check Network tab for 401/404 vs CORS errors349- [ ] Confirm hooks are at component top level350- [ ] Test with cleared localStorage351352### Backend Issues353- [ ] Check backend logs: `docker logs pos-backend-dev --tail 50`354- [ ] Verify JWT_SECRET environment variable355- [ ] Test API endpoints with curl356- [ ] Confirm CORS includes frontend port357- [ ] Check route registration patterns358359### CORS Issues360- [ ] Check current frontend port in terminal361- [ ] Test CORS preflight with curl362- [ ] Verify backend CORS allows current port363- [ ] Restart backend after CORS changes364- [ ] Clear browser cache/storage365366## 🏛️ Security Best Practices367368### Password Security369```go370import "golang.org/x/crypto/bcrypt"371372func HashPassword(password string) (string, error) {373 bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)374 return string(bytes), err375}376377func VerifyPassword(hashedPassword, password string) error {378 return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))379}380```381382### Input Validation383```go384// Validate UUIDs385orderID, err := uuid.Parse(c.Param("id"))386if err != nil {387 c.JSON(http.StatusBadRequest, models.APIResponse{388 Success: false,389 Message: "Invalid order ID format",390 Error: stringPtr("invalid_uuid"),391 })392 return393}394395// Validate enums396validStatuses := []string{"pending", "confirmed", "preparing", "ready", "served", "completed", "cancelled"}397if !contains(validStatuses, req.Status) {398 c.JSON(http.StatusBadRequest, models.APIResponse{399 Success: false,400 Message: "Invalid order status",401 Error: stringPtr("invalid_status"),402 })403 return404}405```406407### Security Logging408```go409// Log security events410log.Printf("LOGIN_SUCCESS: user=%s, ip=%s, role=%s", username, c.ClientIP(), user.Role)411log.Printf("LOGIN_FAILED: username=%s, ip=%s", username, c.ClientIP())412log.Printf("ACCESS_DENIED: user=%s, role=%s, endpoint=%s", username, role, c.Request.URL.Path)413```414415## 🚀 Development Workflow416417### Environment Setup Commands418```bash419# Essential development commands420make dev # Start with proper environment variables421make logs # Monitor authentication flow422make status # Check system health423424# Authentication debugging425make logs-backend # Check JWT processing426make logs-frontend# Check client-side auth427428# Reset authentication state429make db-reset # Fresh database with demo users430make clean # Clear all Docker state431```432433### Demo Accounts for Testing434| Role | Username | Password | Permissions |435|------|----------|----------|-------------|436| **👑 Admin** | `admin` | `admin123` | Full system access |437| **🍽️ Server** | `server1` | `server123` | Dine-in orders only |438| **💰 Counter** | `counter1` | `counter123` | All orders + payments |439| **👨🍳 Kitchen** | `kitchen1` | `kitchen123` | Order preparation |440441### Quick Authentication Test442```typescript443// Test authentication in browser console444console.log('Token:', localStorage.getItem('pos_token'));445console.log('User:', JSON.parse(localStorage.getItem('pos_user') || 'null'));446console.log('API authenticated:', apiClient.isAuthenticated());447448// Clear auth for testing449apiClient.clearAuth();450```
Also in madebyaris/poinf-of-sales
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| madebyaris/poinf-of-sales.cursor/rules/admin-interface-patterns.mdc · 118 | Cursor rules | stylearchsecurityapi+2 | 62/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/api-patterns.mdc · 118 | Cursor rules | lint-formatstylesecuritydatabase+3 | 62/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/backend-golang.mdc · 118 | Cursor rules | testlint-formatstylearch+5 | 69/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/business-logic-patterns.mdc · 118 | Cursor rules | teststyledatabaseperformance+1 | 50/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/database-patterns.mdc · 118 | Cursor rules | stylearchtypessecurity+2 | 62/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/development-workflow.mdc · 118 | Cursor rules | setupbuildteststyle+3 | 86/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/docker-deployment.mdc · 118 | Cursor rules | setupbuildteststyle+8 | 77/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/frontend-react.mdc · 118 | Cursor rules | buildtestlint-formatstyle+4 | 69/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/makefile-scripting.mdc · 118 | Cursor rules | setuplint-formatstylearch+2 | 81/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/performance-optimization-patterns.mdc · 118 | Cursor rules | buildteststyledatabase+3 | 66/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/project-architecture.mdc · 118 | Cursor rules | setupteststylearch+6 | 78/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/react-native-mobile-patterns.mdc · 118 | Cursor rules | buildstylearchui+2 | 74/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/role-based-access-patterns.mdc · 118 | Cursor rules | styletypessecuritydatabase+2 | 58/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/tech-debt-prevention.mdc · 118 | Cursor rules | styletesting-strategyui | 50/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/testing-patterns.mdc · 118 | Cursor rules | setupteststylearch+4 | 74/100 | 3 days ago | |
| madebyaris/poinf-of-sales.cursor/rules/user-journey-optimization.mdc · 118 | Cursor rules | styleperformanceagent-behaviour | 50/100 | 3 days ago |
Diff against .cursor/rules/admin-interface-patterns.mdc Diff against .cursor/rules/api-patterns.mdc Diff against .cursor/rules/backend-golang.mdc Diff against .cursor/rules/business-logic-patterns.mdc Diff against .cursor/rules/database-patterns.mdc Diff against .cursor/rules/development-workflow.mdc Diff against .cursor/rules/docker-deployment.mdc Diff against .cursor/rules/frontend-react.mdc Diff against .cursor/rules/makefile-scripting.mdc Diff against .cursor/rules/performance-optimization-patterns.mdc Diff against .cursor/rules/project-architecture.mdc Diff against .cursor/rules/react-native-mobile-patterns.mdc Diff against .cursor/rules/role-based-access-patterns.mdc Diff against .cursor/rules/tech-debt-prevention.mdc Diff against .cursor/rules/testing-patterns.mdc Diff against .cursor/rules/user-journey-optimization.mdc
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 3 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 3 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 3 days ago |
