From 26f0df58ef61708970117966c824f6e1bc81596a Mon Sep 17 00:00:00 2001 From: goose Date: Sun, 15 Feb 2026 15:44:01 -0300 Subject: [PATCH] fix(backend): Split public and protected routes to fix 401 errors --- backend/AUTH-MIDDLEWARE-FIX.md | 71 ++++++++++++++++++++++++++++++++++ backend/src/main.rs | 20 +++++++--- backend/test-api-remote.sh | 53 +++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 6 deletions(-) create mode 100644 backend/AUTH-MIDDLEWARE-FIX.md create mode 100755 backend/test-api-remote.sh diff --git a/backend/AUTH-MIDDLEWARE-FIX.md b/backend/AUTH-MIDDLEWARE-FIX.md new file mode 100644 index 0000000..9466795 --- /dev/null +++ b/backend/AUTH-MIDDLEWARE-FIX.md @@ -0,0 +1,71 @@ +# ๐Ÿ”ด Critical Issue Found: Auth Middleware Blocking All Requests + +## Problem +ALL API endpoints (including public ones) are returning **401 Unauthorized**. + +## Root Cause +In `main.rs`, the auth middleware was applied to ALL routes using: +```rust +let app = Router::new() + .route("/health", get(handlers::health_check)) // Public! + .route("/api/auth/login", post(handlers::login)) // Public! + .route("/api/users/me", get(handlers::get_profile)) // Protected + .route_layer(axum_middleware::from_fn_with_state( + app_state.clone(), + crate::middleware::auth::jwt_auth_middleware // โ† Applied to ALL routes! + )) + .with_state(app_state); +``` + +The `route_layer` applies the middleware to **all routes** in the router, including public ones like `/health` and `/api/auth/login`. + +## Solution Applied +Split routes into **public** and **protected** routers: + +```rust +// Public routes (no auth required) +let public_routes = Router::new() + .route("/health", get(handlers::health_check)) + .route("/ready", get(handlers::ready_check)) + .route("/api/auth/register", post(handlers::register)) + .route("/api/auth/login", post(handlers::login)) + .route("/api/auth/refresh", post(handlers::refresh_token)) + .route("/api/auth/logout", post(handlers::logout)) + .layer(/* logging and CORS */); + +// Protected routes (auth required) +let protected_routes = Router::new() + .route("/api/users/me", get(handlers::get_profile)) + .route_layer(jwt_auth_middleware) // โ† Only applied to protected routes! + +// Merge them together +let app = public_routes.merge(protected_routes).with_state(app_state); +``` + +## Test Results Before Fix +``` +$ curl http://10.0.10.30:6800/health +HTTP Status: 401 โ† Should be 200! + +$ curl -X POST http://10.0.10.30:6800/api/auth/register +HTTP Status: 401 โ† Public endpoint blocked! +``` + +## Expected Results After Fix +``` +$ curl http://10.0.10.30:6800/health +HTTP Status: 200 โ† OK! + +$ curl -X POST http://10.0.10.30:6800/api/auth/login \ + -H "Content-Type: application/json" \ + -d '{"email": "test@example.com", "password": "SecurePassword123!"}' +HTTP Status: 200 โ† OK! Returns JWT tokens + +$ curl http://10.0.10.30:6800/api/users/me +HTTP Status: 401 โ† Correct! Needs auth token +``` + +## Next Steps +1. Pull the updated code +2. Restart the container: `docker compose restart backend` +3. Test the API: `./test-api-remote.sh` diff --git a/backend/src/main.rs b/backend/src/main.rs index 0a5a1ed..1643a2f 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -75,27 +75,35 @@ async fn main() -> anyhow::Result<()> { }; eprintln!("Building router..."); - let app = Router::new() - // Public endpoints (no auth required) + + // Create separate routers for public and protected routes + let public_routes = Router::new() .route("/health", get(handlers::health_check)) .route("/ready", get(handlers::ready_check)) .route("/api/auth/register", post(handlers::register)) .route("/api/auth/login", post(handlers::login)) .route("/api/auth/refresh", post(handlers::refresh_token)) .route("/api/auth/logout", post(handlers::logout)) - // Protected endpoints (auth required) + .layer( + ServiceBuilder::new() + .layer(TraceLayer::new_for_http()) + .layer(CorsLayer::new()) + ); + + let protected_routes = Router::new() .route("/api/users/me", get(handlers::get_profile)) .layer( ServiceBuilder::new() .layer(TraceLayer::new_for_http()) .layer(CorsLayer::new()) ) - // Apply auth middleware to all routes .route_layer(axum_middleware::from_fn_with_state( app_state.clone(), crate::middleware::auth::jwt_auth_middleware - )) - .with_state(app_state); + )); + + // Merge public and protected routes + let app = public_routes.merge(protected_routes).with_state(app_state); eprintln!("Binding to {}:{}...", config.server.host, config.server.port); let listener = tokio::net::TcpListener::bind(&format!("{}:{}", config.server.host, config.server.port)) diff --git a/backend/test-api-remote.sh b/backend/test-api-remote.sh new file mode 100755 index 0000000..e7ebbb9 --- /dev/null +++ b/backend/test-api-remote.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# API Testing Script for Normogen Backend (Remote Server) +# Server is running at 10.0.10.30:6800 + +echo "๐Ÿงช Testing Normogen API Endpoints at http://10.0.10.30:6800" +echo "================================================================" +echo "" + +# Test 1: Health Check +echo "1. Testing Health Check..." +HEALTH=$(curl -s -w " +HTTP Status: %{http_code} +" http://10.0.10.30:6800/health) +echo "$HEALTH" +echo "" + +# Test 2: Ready Check +echo "2. Testing Ready Check..." +READY=$(curl -s -w " +HTTP Status: %{http_code} +" http://10.0.10.30:6800/ready) +echo "$READY" +echo "" + +# Test 3: Register User +echo "3. Testing User Registration..." +REGISTER=$(curl -s -w " +HTTP Status: %{http_code} +" -X POST http://10.0.10.30:6800/api/auth/register \ + -H "Content-Type: application/json" \ + -d '{"email": "test@example.com", "password": "SecurePassword123!", "username": "testuser"}') +echo "$REGISTER" +echo "" + +# Test 4: Login +echo "4. Testing User Login..." +LOGIN=$(curl -s -w " +HTTP Status: %{http_code} +" -X POST http://10.0.10.30:6800/api/auth/login \ + -H "Content-Type: application/json" \ + -d '{"email": "test@example.com", "password": "SecurePassword123!"}') +echo "$LOGIN" +echo "" + +# Test 5: Protected Endpoint (should fail without auth) +echo "5. Testing Protected Endpoint (without auth, should fail)..." +PROFILE=$(curl -s -w " +HTTP Status: %{http_code} +" http://10.0.10.30:6800/api/users/me) +echo "$PROFILE" +echo "" + +echo "โœ… Tests complete!"