Mirror Job-Info-Test PB token validation flow

This commit is contained in:
2026-03-29 03:29:19 +00:00
parent a045a1fbd8
commit d70a8b64c6
2 changed files with 86 additions and 4 deletions
+55 -2
View File
@@ -57,6 +57,14 @@ async function createValidatedPbClient(token: string): Promise<PocketBase> {
return client; 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<string, unknown> | null { function decodeJwtPayload(token: string): Record<string, unknown> | null {
try { try {
const parts = String(token || '').split('.'); 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 // GET /api/tasks/users
// Requires: x-pb-token // Requires: x-pb-token
app.get('/api/tasks/users', async (c) => { 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); if (!pbToken) return c.json({ success: false, message: 'Missing x-pb-token' }, 401);
let taskPb: PocketBase; let taskPb: PocketBase;
@@ -526,7 +579,7 @@ app.get('/api/tasks/users', async (c) => {
// GET /api/tasks/list?userId=<id> // GET /api/tasks/list?userId=<id>
// Requires: x-pb-token // Requires: x-pb-token
app.get('/api/tasks/list', async (c) => { 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); if (!pbToken) return c.json({ success: false, message: 'Missing x-pb-token' }, 401);
let taskPb: PocketBase; let taskPb: PocketBase;
+31 -2
View File
@@ -303,11 +303,35 @@
return pb.authStore.token; 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() { async function loadUsers() {
await ensurePocketBaseClient(); await ensurePocketBaseClient();
const token = getCurrentToken(); const token = getCurrentToken();
const resp = await fetch('/api/tasks/users', { 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(() => ({})); const data = await resp.json().catch(() => ({}));
if (!resp.ok || !data?.success) { if (!resp.ok || !data?.success) {
@@ -389,7 +413,10 @@
? `/api/tasks/list?${query.toString()}` ? `/api/tasks/list?${query.toString()}`
: '/api/tasks/list'; : '/api/tasks/list';
const resp = await fetch(url, { const resp = await fetch(url, {
headers: { 'x-pb-token': token }, headers: {
'x-pb-token': token,
'Authorization': `Bearer ${token}`,
},
}); });
const data = await resp.json().catch(() => ({})); const data = await resp.json().catch(() => ({}));
if (!resp.ok || !data?.success) { if (!resp.ok || !data?.success) {
@@ -433,6 +460,8 @@
await updatePbStatus(); await updatePbStatus();
if (client.authStore?.isValid) { if (client.authStore?.isValid) {
const tokenValidation = await validateCurrentToken();
writeOutput({ message: 'PocketBase token validated', user: tokenValidation?.user || null, tokenContext: tokenValidation?.tokenContext || undefined });
await loadUsers(); await loadUsers();
await loadTasks(); await loadTasks();
} }