fix(backend): Split public and protected routes to fix 401 errors
This commit is contained in:
parent
e5d0ae4fd1
commit
26f0df58ef
3 changed files with 138 additions and 6 deletions
71
backend/AUTH-MIDDLEWARE-FIX.md
Normal file
71
backend/AUTH-MIDDLEWARE-FIX.md
Normal file
|
|
@ -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`
|
||||||
|
|
@ -75,27 +75,35 @@ async fn main() -> anyhow::Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
eprintln!("Building router...");
|
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("/health", get(handlers::health_check))
|
||||||
.route("/ready", get(handlers::ready_check))
|
.route("/ready", get(handlers::ready_check))
|
||||||
.route("/api/auth/register", post(handlers::register))
|
.route("/api/auth/register", post(handlers::register))
|
||||||
.route("/api/auth/login", post(handlers::login))
|
.route("/api/auth/login", post(handlers::login))
|
||||||
.route("/api/auth/refresh", post(handlers::refresh_token))
|
.route("/api/auth/refresh", post(handlers::refresh_token))
|
||||||
.route("/api/auth/logout", post(handlers::logout))
|
.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))
|
.route("/api/users/me", get(handlers::get_profile))
|
||||||
.layer(
|
.layer(
|
||||||
ServiceBuilder::new()
|
ServiceBuilder::new()
|
||||||
.layer(TraceLayer::new_for_http())
|
.layer(TraceLayer::new_for_http())
|
||||||
.layer(CorsLayer::new())
|
.layer(CorsLayer::new())
|
||||||
)
|
)
|
||||||
// Apply auth middleware to all routes
|
|
||||||
.route_layer(axum_middleware::from_fn_with_state(
|
.route_layer(axum_middleware::from_fn_with_state(
|
||||||
app_state.clone(),
|
app_state.clone(),
|
||||||
crate::middleware::auth::jwt_auth_middleware
|
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);
|
eprintln!("Binding to {}:{}...", config.server.host, config.server.port);
|
||||||
let listener = tokio::net::TcpListener::bind(&format!("{}:{}", config.server.host, config.server.port))
|
let listener = tokio::net::TcpListener::bind(&format!("{}:{}", config.server.host, config.server.port))
|
||||||
|
|
|
||||||
53
backend/test-api-remote.sh
Executable file
53
backend/test-api-remote.sh
Executable file
|
|
@ -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!"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue