openreader/src/app/api/auth/route.ts
Sunny 567b29abf2 fix: address CodeRabbit review comments for PR #76
Security fixes:
- Validate returnTo parameter in auth route to prevent open redirect attacks

Performance fixes:
- Add concurrency limit (3) for EPUB spine extraction to prevent memory exhaustion
- Iteratively split oversized paragraphs in chunk splitting to prevent content loss
- Add iteration limit (10) and shrink-check to summary combining loop

Data integrity:
- Remap summary rows when document IDs change during sync

UX improvements:
- Clear summary base URL when switching to non-custom providers
- Add type="button" to SummarizeButton to prevent form submission
- Add dev logging to PDFViewer error handler
- Move document state detection from render to useEffect

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 04:48:31 +00:00

40 lines
1.3 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { getAuthToken } from '@/lib/auth';
function isValidReturnTo(returnTo: string): boolean {
// Only allow relative paths starting with /
// Reject absolute URLs, protocol-relative URLs, and other schemes
if (!returnTo.startsWith('/')) return false;
if (returnTo.startsWith('//')) return false;
// Reject URLs with encoded characters that could bypass checks
if (returnTo.includes('%')) {
try {
const decoded = decodeURIComponent(returnTo);
if (decoded.startsWith('//') || decoded.includes('://')) return false;
} catch {
return false;
}
}
return true;
}
export async function GET(request: NextRequest) {
const token = request.nextUrl.searchParams.get('token');
const returnTo = request.nextUrl.searchParams.get('returnTo') || '/';
if (token !== getAuthToken()) {
return NextResponse.json({ error: 'Invalid token' }, { status: 401 });
}
// Validate returnTo to prevent open redirect attacks
const safeReturnTo = isValidReturnTo(returnTo) ? returnTo : '/';
const response = NextResponse.redirect(new URL(safeReturnTo, request.url));
response.cookies.set('auth_session', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 60 * 60 * 24 * 30,
path: '/',
});
return response;
}