fix: require auth on cookie-picker data routes (CRITICAL-01)

- Add Bearer token auth gate on all /cookie-picker/* data/action routes
- GET /cookie-picker HTML page stays unauthenticated (UI shell)
- Token embedded in served HTML for picker's fetch calls
- CORS preflight now allows Authorization header
This commit is contained in:
Garry Tan
2026-03-27 22:13:48 -07:00
parent e16bf23ca0
commit 480f4bb23d
2 changed files with 19 additions and 4 deletions
+14 -2
View File
@@ -53,6 +53,7 @@ export async function handleCookiePickerRoute(
url: URL,
req: Request,
bm: BrowserManager,
authToken?: string,
): Promise<Response> {
const pathname = url.pathname;
const port = parseInt(url.port, 10) || 9400;
@@ -64,7 +65,7 @@ export async function handleCookiePickerRoute(
headers: {
'Access-Control-Allow-Origin': corsOrigin(port),
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
});
}
@@ -72,13 +73,24 @@ export async function handleCookiePickerRoute(
try {
// GET /cookie-picker — serve the picker UI
if (pathname === '/cookie-picker' && req.method === 'GET') {
const html = getCookiePickerHTML(port);
const html = getCookiePickerHTML(port, authToken);
return new Response(html, {
status: 200,
headers: { 'Content-Type': 'text/html; charset=utf-8' },
});
}
// ─── Auth gate: all data/action routes below require Bearer token ───
if (authToken) {
const authHeader = req.headers.get('authorization');
if (!authHeader || authHeader !== `Bearer ${authToken}`) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
});
}
}
// GET /cookie-picker/browsers — list installed browsers
if (pathname === '/cookie-picker/browsers' && req.method === 'GET') {
const browsers = findInstalledBrowsers();
+5 -2
View File
@@ -7,7 +7,7 @@
* No cookie values exposed anywhere.
*/
export function getCookiePickerHTML(serverPort: number): string {
export function getCookiePickerHTML(serverPort: number, authToken?: string): string {
const baseUrl = `http://127.0.0.1:${serverPort}`;
return `<!DOCTYPE html>
@@ -330,6 +330,7 @@ export function getCookiePickerHTML(serverPort: number): string {
<script>
(function() {
const BASE = '${baseUrl}';
const AUTH_TOKEN = '${authToken || ''}';
let activeBrowser = null;
let activeProfile = 'Default';
let allProfiles = [];
@@ -372,7 +373,9 @@ export function getCookiePickerHTML(serverPort: number): string {
// ─── API ────────────────────────────────
async function api(path, opts) {
const res = await fetch(BASE + '/cookie-picker' + path, opts);
const headers = { ...(opts?.headers || {}) };
if (AUTH_TOKEN) headers['Authorization'] = 'Bearer ' + AUTH_TOKEN;
const res = await fetch(BASE + '/cookie-picker' + path, { ...opts, headers });
const data = await res.json();
if (!res.ok) {
const err = new Error(data.error || 'Request failed');