This commit is contained in:
+29
-2
@@ -22,9 +22,16 @@ export interface ServerOptions {
|
|||||||
export async function createServer(opts: ServerOptions) {
|
export async function createServer(opts: ServerOptions) {
|
||||||
const fastify = Fastify({ logger: true });
|
const fastify = Fastify({ logger: true });
|
||||||
|
|
||||||
// CORS
|
// CORS — restrictive origin list
|
||||||
await fastify.register(cors, {
|
await fastify.register(cors, {
|
||||||
origin: true,
|
origin: (origin: string | undefined, cb: (err: Error | null, allow: boolean) => void) => {
|
||||||
|
// Allow requests with no origin (same-origin, curl, etc.)
|
||||||
|
if (!origin) return cb(null, true);
|
||||||
|
// Allow known origins
|
||||||
|
const allowed = ['https://web-cad-neu.server.media-on.de', 'http://localhost:5173', 'http://localhost:8082'];
|
||||||
|
if (allowed.includes(origin)) return cb(null, true);
|
||||||
|
cb(new Error('Not allowed by CORS'), false);
|
||||||
|
},
|
||||||
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -62,6 +69,26 @@ export async function createServer(opts: ServerOptions) {
|
|||||||
|
|
||||||
const authService = new AuthService(opts.db);
|
const authService = new AuthService(opts.db);
|
||||||
|
|
||||||
|
// Auth middleware — protect all /api/ routes except auth login/register
|
||||||
|
fastify.addHook('onRequest', async (request: any, reply: any) => {
|
||||||
|
const url = request.url;
|
||||||
|
// Skip auth endpoints and health check
|
||||||
|
if (url === '/api/auth/login' || url === '/api/auth/register' || url === '/api/health') return;
|
||||||
|
// Skip non-API routes (static files, WebSocket)
|
||||||
|
if (!url.startsWith('/api/')) return;
|
||||||
|
|
||||||
|
const auth = request.headers.authorization;
|
||||||
|
if (!auth || !auth.startsWith('Bearer ')) {
|
||||||
|
return reply.code(401).send({ error: 'Authentication required' });
|
||||||
|
}
|
||||||
|
const token = auth.slice(7);
|
||||||
|
const user = authService.getUserFromSession(token);
|
||||||
|
if (!user) {
|
||||||
|
return reply.code(401).send({ error: 'Invalid or expired session' });
|
||||||
|
}
|
||||||
|
(request as any).user = user;
|
||||||
|
});
|
||||||
|
|
||||||
registerAuthRoutes(fastify, authService);
|
registerAuthRoutes(fastify, authService);
|
||||||
registerProjectRoutes(fastify, opts.db);
|
registerProjectRoutes(fastify, opts.db);
|
||||||
registerDrawingRoutes(fastify, opts.db);
|
registerDrawingRoutes(fastify, opts.db);
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# Test Report — Auth Middleware & CORS Fix (Issue #1 + #3)
|
||||||
|
|
||||||
|
**Date:** 2026-06-29
|
||||||
|
**Task:** Fix Issue #1 (No Authentication on CRUD Routes) + Issue #3 (CORS) from CODE_ANALYSIS.md
|
||||||
|
**File modified:** `backend/src/server.ts`
|
||||||
|
|
||||||
|
## Build Verification
|
||||||
|
|
||||||
|
```
|
||||||
|
cd /a0/usr/workdir/web-cad-neu/backend && npx tsc --noEmit
|
||||||
|
```
|
||||||
|
**Result:** ✅ PASS — No type errors, exit code 0
|
||||||
|
|
||||||
|
## Test Results
|
||||||
|
|
||||||
|
### Test 1: Unauthenticated /api/projects → 401
|
||||||
|
```
|
||||||
|
curl -s -o /dev/null -w '%{http_code}' http://localhost:3001/api/projects
|
||||||
|
```
|
||||||
|
**Expected:** 401
|
||||||
|
**Actual:** 401 ✅
|
||||||
|
|
||||||
|
### Test 2: Health check /api/health → 200
|
||||||
|
```
|
||||||
|
curl -s -o /dev/null -w '%{http_code}' http://localhost:3001/api/health
|
||||||
|
```
|
||||||
|
**Expected:** 200
|
||||||
|
**Actual:** 200 ✅
|
||||||
|
|
||||||
|
### Test 3: Register endpoint accessible (no auth required)
|
||||||
|
```
|
||||||
|
curl -s -X POST http://localhost:3001/api/auth/register -H 'Content-Type: application/json' \
|
||||||
|
-d '{"email":"test@test.de","password":"test1234","name":"Test User"}'
|
||||||
|
```
|
||||||
|
**Expected:** 201
|
||||||
|
**Actual:** 201 ✅ (user created, session token returned)
|
||||||
|
|
||||||
|
### Test 4: Login endpoint accessible (no auth required)
|
||||||
|
```
|
||||||
|
curl -s -w '\nHTTP_CODE:%{http_code}' -X POST http://localhost:3001/api/auth/login \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"email":"test@test.de","password":"test1234"}'
|
||||||
|
```
|
||||||
|
**Expected:** 200
|
||||||
|
**Actual:** 200 ✅ (user + session token returned)
|
||||||
|
|
||||||
|
### Test 5: Authenticated /api/projects → 200
|
||||||
|
```
|
||||||
|
TOKEN=<valid_session_token>
|
||||||
|
curl -s -w '\nHTTP_CODE:%{http_code}' http://localhost:3001/api/projects \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
|
```
|
||||||
|
**Expected:** 200
|
||||||
|
**Actual:** 200 ✅ (project list returned)
|
||||||
|
|
||||||
|
## Smoke Test
|
||||||
|
|
||||||
|
- Backend starts without errors: ✅
|
||||||
|
- Server listens on port 3001: ✅
|
||||||
|
- Yjs persistence initialized: ✅
|
||||||
|
- Auth middleware correctly blocks unauthenticated /api/ requests: ✅
|
||||||
|
- Auth middleware correctly skips /api/auth/login, /api/auth/register, /api/health: ✅
|
||||||
|
- Auth middleware correctly skips non-/api/ routes (static files): ✅
|
||||||
|
- Valid Bearer token grants access to protected routes: ✅
|
||||||
|
- CORS restricted to allowed origins (web-cad-neu.server.media-on.de, localhost:5173, localhost:8082): ✅
|
||||||
|
|
||||||
|
## Changes Summary
|
||||||
|
|
||||||
|
### server.ts — Auth Middleware (Issue #1)
|
||||||
|
Added `onRequest` hook after `authService` creation, before route registrations:
|
||||||
|
- Skips `/api/auth/login`, `/api/auth/register`, `/api/health`
|
||||||
|
- Skips all non-`/api/` routes (static files, WebSocket)
|
||||||
|
- Extracts Bearer token from Authorization header
|
||||||
|
- Validates via `authService.getUserFromSession(token)`
|
||||||
|
- Returns 401 if no token or invalid session
|
||||||
|
- Attaches user to `(request as any).user` for route handlers
|
||||||
|
|
||||||
|
### server.ts — CORS Fix (Issue #3)
|
||||||
|
Changed `origin: true` to restrictive config:
|
||||||
|
- Allows requests with no origin (same-origin, curl)
|
||||||
|
- Allows known origins: `https://web-cad-neu.server.media-on.de`, `http://localhost:5173`, `http://localhost:8082`
|
||||||
|
- Rejects all other origins
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
- None
|
||||||
Reference in New Issue
Block a user