Skip to content

Commit 59928cc

Browse files
SchenLongclaude
andcommitted
fix: resolve QA report issues and implement security enhancements
Fixes from QA-REPORT-2026-02-19: CRITICAL FIXES: - Fix authentication middleware session recognition bug - Add JWT-based middleware auth token for Edge Runtime compatibility - Password login sessions now work with protected routes - middleware.ts: verifyMiddlewareToken() checks JWT tokens SECURITY ENHANCEMENTS: - Remove JWT_SECRET fallback to SESSION_SECRET (security boundary separation) - Add JWT verification error logging for security monitoring - Add input sanitization to Settings API (XSS prevention) - Add Content-Type validation to POST/PATCH endpoints - Change cookie sameSite default from 'lax' to 'strict' (CSRF protection) - Add session ID format validation (cuid format check) NEW API ENDPOINTS: - Implement /api/sessions (list user sessions) - Implement /api/sessions/[sessionId] (revoke session) - Implement /api/settings (get/update user settings) - Implement /api/missions (list/create missions) - Implement /api/missions/[missionId] (get/update/delete missions) NEW PAGES: - Add /chat placeholder page with ChatInterface integration - Add /party placeholder page for multi-agent collaboration REMOVED: - Remove duplicate /agents/roster page (use dashboard route instead) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent cacd428 commit 59928cc

12 files changed

Lines changed: 1312 additions & 24 deletions

File tree

apps/web-ui/src/app/agents/roster/page.tsx

Lines changed: 0 additions & 12 deletions
This file was deleted.

apps/web-ui/src/app/api/auth/login/route.ts

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ function extractRequestMetadata(request: NextRequest): RequestMetadata {
5757

5858
export async function POST(request: NextRequest) {
5959
try {
60+
// Validate Content-Type header
61+
const contentType = request.headers.get('content-type');
62+
if (!contentType?.includes('application/json')) {
63+
return NextResponse.json(
64+
{
65+
error: 'Unsupported Media Type',
66+
message: 'Content-Type must be application/json',
67+
},
68+
{ status: 415 }
69+
);
70+
}
71+
6072
// Parse and validate request body
6173
const body = await request.json();
6274
const validationResult = loginSchema.safeParse(body);
@@ -133,7 +145,19 @@ export async function POST(request: NextRequest) {
133145
// Generate middleware auth token (JWT for Edge Runtime compatibility)
134146
// This token is used by middleware to verify authentication without DB access
135147
// Includes onboarding status to avoid database lookups in middleware
136-
const middlewareSecret = new TextEncoder().encode(process.env.JWT_SECRET || process.env.SESSION_SECRET);
148+
const jwtSecret = process.env.JWT_SECRET;
149+
if (!jwtSecret) {
150+
console.error('[Login] JWT_SECRET not configured');
151+
return NextResponse.json(
152+
{
153+
error: 'Configuration error',
154+
message: 'Authentication service not properly configured',
155+
},
156+
{ status: 500 }
157+
);
158+
}
159+
160+
const middlewareSecret = new TextEncoder().encode(jwtSecret);
137161
const middlewareToken = await new SignJWT({
138162
userId: user.id,
139163
email: user.email,
@@ -155,11 +179,16 @@ export async function POST(request: NextRequest) {
155179
},
156180
});
157181

158-
// Set middleware auth token cookie (HttpOnly for security)
182+
// SECURITY: Set middleware auth token cookie with strict security defaults
183+
// - httpOnly: Prevents JavaScript access (XSS protection)
184+
// - secure: Only send over HTTPS in production
185+
// - sameSite: 'strict' provides best CSRF protection
186+
// - path: '/' ensures cookie is available on all routes
187+
const isProduction = process.env.NODE_ENV === 'production';
159188
response.cookies.set('middleware_auth', middlewareToken, {
160189
httpOnly: true,
161-
secure: process.env.COOKIE_SECURE === 'true',
162-
sameSite: (process.env.COOKIE_SAMESITE as 'strict' | 'lax' | 'none') || 'lax',
190+
secure: isProduction || process.env.COOKIE_SECURE === 'true',
191+
sameSite: (process.env.COOKIE_SAMESITE as 'strict' | 'lax' | 'none') || 'strict',
163192
maxAge: 15 * 24 * 60 * 60, // 15 days
164193
path: '/',
165194
});

apps/web-ui/src/app/api/auth/logout/route.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,24 @@ export async function POST() {
1313
// Destroy session (clears cookie and deletes from database)
1414
await destroySession();
1515

16-
return NextResponse.json(
16+
// Create response and also clear the middleware_auth cookie
17+
const response = NextResponse.json(
1718
{
1819
message: 'Logged out successfully',
1920
},
2021
{ status: 200 }
2122
);
23+
24+
// Clear middleware auth token cookie
25+
response.cookies.set('middleware_auth', '', {
26+
httpOnly: true,
27+
secure: process.env.COOKIE_SECURE === 'true',
28+
sameSite: (process.env.COOKIE_SAMESITE as 'strict' | 'lax' | 'none') || 'lax',
29+
maxAge: 0,
30+
path: '/',
31+
});
32+
33+
return response;
2234
} catch (error) {
2335
console.error('Logout error:', error);
2436
return NextResponse.json(

0 commit comments

Comments
 (0)