forked from mirror/crumbs-original
Bearer token authentication now works on all /api/* routes, not just /api/mcp. If a Bearer token is present it's validated first; otherwise session cookie auth is used as fallback. Updates settings UI to reflect the broader scope (tab renamed to "API", REST API curl example added) and updates the landing page feature card. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
82 lines
2.0 KiB
TypeScript
82 lines
2.0 KiB
TypeScript
import type { Handle } from '@sveltejs/kit';
|
|
import { redirect } from '@sveltejs/kit';
|
|
import { validateSession, isSetupComplete } from '$lib/server/auth.js';
|
|
import { validateApiKey, getUserForApiKey } from '$lib/server/api-keys.js';
|
|
|
|
const PUBLIC_PATHS = [
|
|
'/login',
|
|
'/setup',
|
|
'/api/auth/login',
|
|
'/api/auth/setup',
|
|
'/api/auth/oauth'
|
|
];
|
|
|
|
export const handle: Handle = async ({ event, resolve }) => {
|
|
const { pathname } = event.url;
|
|
|
|
// Initialize locals
|
|
event.locals.user = null;
|
|
|
|
// Allow public paths
|
|
if (PUBLIC_PATHS.some((p) => pathname.startsWith(p))) {
|
|
return resolve(event);
|
|
}
|
|
|
|
// API routes: authenticate via Bearer token if present
|
|
if (pathname.startsWith('/api/')) {
|
|
const authHeader = event.request.headers.get('authorization');
|
|
const token = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null;
|
|
|
|
if (token) {
|
|
const { valid, userId } = validateApiKey(token);
|
|
if (!valid || !userId) {
|
|
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
|
|
status: 401,
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
|
|
const user = getUserForApiKey(userId);
|
|
if (!user) {
|
|
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
|
|
status: 401,
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
|
|
event.locals.user = user;
|
|
return resolve(event);
|
|
}
|
|
}
|
|
|
|
// Check if setup is complete
|
|
const setupDone = await isSetupComplete();
|
|
if (!setupDone) {
|
|
if (pathname !== '/setup') {
|
|
throw redirect(302, '/setup');
|
|
}
|
|
return resolve(event);
|
|
}
|
|
|
|
// Validate session and populate user
|
|
const sessionToken = event.cookies.get('session');
|
|
if (sessionToken) {
|
|
const result = await validateSession(sessionToken);
|
|
if (result.valid && result.user) {
|
|
event.locals.user = result.user;
|
|
}
|
|
}
|
|
|
|
if (!event.locals.user) {
|
|
if (pathname.startsWith('/api/')) {
|
|
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
|
|
status: 401,
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
throw redirect(302, '/login');
|
|
}
|
|
|
|
return resolve(event);
|
|
};
|