diff --git a/server.ts b/server.ts index 9b02307..d96fe70 100644 --- a/server.ts +++ b/server.ts @@ -57,6 +57,14 @@ async function createValidatedPbClient(token: string): Promise { return client; } +function getRequestPbToken(c: any): string { + const headerToken = String(c.req.header('x-pb-token') || '').trim(); + if (headerToken) return headerToken; + const authHeader = String(c.req.header('authorization') || '').trim(); + const bearer = getBearerToken(authHeader); + return String(bearer || '').trim(); +} + function decodeJwtPayload(token: string): Record | null { try { const parts = String(token || '').split('.'); @@ -454,10 +462,55 @@ app.get('/api/auth/msal-config', (c) => { }); }); +app.post('/api/auth/validate-pb-token', async (c) => { + try { + const body = await c.req.json().catch(() => ({})); + const pbToken = String(body?.pbToken || '').trim(); + if (!pbToken) { + return c.json({ valid: false, error: 'No token provided' }, 400); + } + + const tokenPayload = decodeJwtPayload(pbToken); + let taskPb: PocketBase; + try { + taskPb = await createValidatedPbClient(pbToken); + } catch (error) { + const message = (error as Error)?.message || String(error); + return c.json({ + valid: false, + error: 'Invalid PocketBase token', + details: message, + tokenContext: { + type: String(tokenPayload?.type || ''), + collectionId: String(tokenPayload?.collectionId || ''), + collectionName: String(tokenPayload?.collectionName || ''), + }, + }, 401); + } + + const user = taskPb.authStore.record || taskPb.authStore.model || null; + return c.json({ + valid: true, + user: user ? { + id: user.id || '', + email: user.email || '', + name: user.name || user.username || user.email || '', + } : null, + tokenContext: { + type: String(tokenPayload?.type || ''), + collectionId: String(tokenPayload?.collectionId || ''), + collectionName: String(tokenPayload?.collectionName || ''), + }, + }); + } catch (error) { + return c.json({ valid: false, error: (error as Error)?.message || 'Validation failed' }, 500); + } +}); + // GET /api/tasks/users // Requires: x-pb-token app.get('/api/tasks/users', async (c) => { - const pbToken = c.req.header('x-pb-token'); + const pbToken = getRequestPbToken(c); if (!pbToken) return c.json({ success: false, message: 'Missing x-pb-token' }, 401); let taskPb: PocketBase; @@ -526,7 +579,7 @@ app.get('/api/tasks/users', async (c) => { // GET /api/tasks/list?userId= // Requires: x-pb-token app.get('/api/tasks/list', async (c) => { - const pbToken = c.req.header('x-pb-token'); + const pbToken = getRequestPbToken(c); if (!pbToken) return c.json({ success: false, message: 'Missing x-pb-token' }, 401); let taskPb: PocketBase; diff --git a/tasks.html b/tasks.html index 03d43c6..ef53208 100644 --- a/tasks.html +++ b/tasks.html @@ -303,11 +303,35 @@ return pb.authStore.token; } + async function validateCurrentToken() { + await ensurePocketBaseClient(); + const token = getCurrentToken(); + const resp = await fetch('/api/auth/validate-pb-token', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ pbToken: token }), + }); + const data = await resp.json().catch(() => ({})); + if (!resp.ok || data?.valid !== true) { + const details = data?.details + ? (typeof data.details === 'string' ? data.details : JSON.stringify(data.details)) + : ''; + const context = data?.tokenContext ? ` tokenContext=${JSON.stringify(data.tokenContext)}` : ''; + throw new Error(details + ? `${data?.error || 'PocketBase token invalid'}: ${details}${context}` + : `${data?.error || 'PocketBase token invalid'}${context}`); + } + return data; + } + async function loadUsers() { await ensurePocketBaseClient(); const token = getCurrentToken(); const resp = await fetch('/api/tasks/users', { - headers: { 'x-pb-token': token }, + headers: { + 'x-pb-token': token, + 'Authorization': `Bearer ${token}`, + }, }); const data = await resp.json().catch(() => ({})); if (!resp.ok || !data?.success) { @@ -389,7 +413,10 @@ ? `/api/tasks/list?${query.toString()}` : '/api/tasks/list'; const resp = await fetch(url, { - headers: { 'x-pb-token': token }, + headers: { + 'x-pb-token': token, + 'Authorization': `Bearer ${token}`, + }, }); const data = await resp.json().catch(() => ({})); if (!resp.ok || !data?.success) { @@ -433,6 +460,8 @@ await updatePbStatus(); if (client.authStore?.isValid) { + const tokenValidation = await validateCurrentToken(); + writeOutput({ message: 'PocketBase token validated', user: tokenValidation?.user || null, tokenContext: tokenValidation?.tokenContext || undefined }); await loadUsers(); await loadTasks(); }