Mirror Job-Info-Test PB token validation flow
This commit is contained in:
@@ -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
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user