fix(frontend): correctly resolve token object from revealWorkspaceApiKey to prevent unauthorized access
This commit is contained in:
@@ -6,27 +6,42 @@
|
||||
* - vibn_sk_... API key: returns just the one workspace the key is bound to
|
||||
*/
|
||||
|
||||
import { NextResponse } from 'next/server';
|
||||
import { authSession } from '@/lib/auth/session-server';
|
||||
import { queryOne } from '@/lib/db-postgres';
|
||||
import { ensureWorkspaceForUser, listWorkspacesForUser } from '@/lib/workspaces';
|
||||
import { requireWorkspacePrincipal, listWorkspaceApiKeys, mintWorkspaceApiKey, revealWorkspaceApiKey } from '@/lib/auth/workspace-auth';
|
||||
import { NextResponse } from "next/server";
|
||||
import { authSession } from "@/lib/auth/session-server";
|
||||
import { queryOne } from "@/lib/db-postgres";
|
||||
import {
|
||||
ensureWorkspaceForUser,
|
||||
listWorkspacesForUser,
|
||||
} from "@/lib/workspaces";
|
||||
import {
|
||||
requireWorkspacePrincipal,
|
||||
listWorkspaceApiKeys,
|
||||
mintWorkspaceApiKey,
|
||||
revealWorkspaceApiKey,
|
||||
} from "@/lib/auth/workspace-auth";
|
||||
|
||||
export async function GET(request: Request) {
|
||||
if (request.headers.get('authorization')?.toLowerCase().startsWith('bearer vibn_sk_')) {
|
||||
if (
|
||||
request.headers
|
||||
.get("authorization")
|
||||
?.toLowerCase()
|
||||
.startsWith("bearer vibn_sk_")
|
||||
) {
|
||||
const principal = await requireWorkspacePrincipal(request);
|
||||
if (principal instanceof NextResponse) return principal;
|
||||
return NextResponse.json({ workspaces: [serializeWorkspace(principal.workspace)] });
|
||||
return NextResponse.json({
|
||||
workspaces: [serializeWorkspace(principal.workspace)],
|
||||
});
|
||||
}
|
||||
|
||||
const session = await authSession();
|
||||
if (!session?.user?.email) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const userRow = await queryOne<{ id: string }>(
|
||||
`SELECT id FROM fs_users WHERE data->>'email' = $1 LIMIT 1`,
|
||||
[session.user.email]
|
||||
[session.user.email],
|
||||
);
|
||||
if (!userRow) {
|
||||
return NextResponse.json({ workspaces: [] });
|
||||
@@ -45,37 +60,57 @@ export async function GET(request: Request) {
|
||||
});
|
||||
list = await listWorkspacesForUser(userRow.id);
|
||||
} catch (err) {
|
||||
console.error('[api/workspaces] lazy ensure failed', err);
|
||||
console.error("[api/workspaces] lazy ensure failed", err);
|
||||
}
|
||||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
const includeDefaultToken = url.searchParams.get('include_default_token') === 'true';
|
||||
const includeDefaultToken =
|
||||
url.searchParams.get("include_default_token") === "true";
|
||||
|
||||
if (includeDefaultToken && list.length > 0) {
|
||||
const ws = list[0];
|
||||
let defaultToken: string | null = null;
|
||||
try {
|
||||
const keys = await listWorkspaceApiKeys(ws.id);
|
||||
let defaultKey = keys.find((k: any) => k.name === 'default' && !k.revoked_at);
|
||||
let defaultKey = keys.find(
|
||||
(k: any) => k.name === "default" && !k.revoked_at,
|
||||
);
|
||||
if (!defaultKey) {
|
||||
const minted = await mintWorkspaceApiKey({ workspaceId: ws.id, name: 'default', createdBy: userRow!.id, scopes: ['workspace:*'] });
|
||||
const minted = await mintWorkspaceApiKey({
|
||||
workspaceId: ws.id,
|
||||
name: "default",
|
||||
createdBy: userRow!.id,
|
||||
scopes: ["workspace:*"],
|
||||
});
|
||||
defaultToken = minted.token;
|
||||
} else {
|
||||
defaultToken = await revealWorkspaceApiKey(ws.id, defaultKey.id);
|
||||
if (!defaultToken) {
|
||||
const minted = await mintWorkspaceApiKey({ workspaceId: ws.id, name: 'default', createdBy: userRow!.id, scopes: ['workspace:*'] });
|
||||
const revealed = await revealWorkspaceApiKey(ws.id, defaultKey.id);
|
||||
if (revealed) {
|
||||
defaultToken = revealed.token;
|
||||
} else {
|
||||
const minted = await mintWorkspaceApiKey({
|
||||
workspaceId: ws.id,
|
||||
name: "default",
|
||||
createdBy: userRow!.id,
|
||||
scopes: ["workspace:*"],
|
||||
});
|
||||
defaultToken = minted.token;
|
||||
}
|
||||
}
|
||||
} catch { /* non-fatal */ }
|
||||
return NextResponse.json({ workspaces: list.map(serializeWorkspace), defaultToken });
|
||||
} catch {
|
||||
/* non-fatal */
|
||||
}
|
||||
return NextResponse.json({
|
||||
workspaces: list.map(serializeWorkspace),
|
||||
defaultToken,
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({ workspaces: list.map(serializeWorkspace) });
|
||||
}
|
||||
|
||||
function serializeWorkspace(w: import('@/lib/workspaces').VibnWorkspace) {
|
||||
function serializeWorkspace(w: import("@/lib/workspaces").VibnWorkspace) {
|
||||
return {
|
||||
id: w.id,
|
||||
slug: w.slug,
|
||||
|
||||
Reference in New Issue
Block a user