WFDASH-WEBAPP/src/lib/server/browser-session.ts

89 lines
2.5 KiB
TypeScript

import type { NextRequest } from "next/server";
/**
* Validates a browser-forwarded PocketBase auth token against PocketBase.
*
* Dashboard auth lives in localStorage, so Route Handlers cannot read it from
* cookies. Browser callers send the current PB token in `Authorization`; this
* helper asks PB to refresh/validate it instead of trusting or decoding the
* token locally.
*/
export async function assertBrowserSession(req: NextRequest): Promise<void> {
const authorization = req.headers.get("authorization") || "";
const token = authorization.startsWith("Bearer ")
? authorization.slice(7)
: authorization;
if (!token) {
throw Object.assign(new Error("Not authenticated."), { status: 401 });
}
const pbUrl =
process.env.NEXT_PUBLIC_POCKETBASE_URL || "http://127.0.0.1:8090";
const response = await fetch(
`${pbUrl.replace(/\/$/, "")}/api/collections/users/auth-refresh`,
{
method: "POST",
headers: { Authorization: token },
signal: AbortSignal.timeout(5_000),
},
);
if (!response.ok) {
throw Object.assign(new Error("Invalid or expired session."), {
status: 401,
});
}
}
export interface BrowserSessionUser {
id: string;
user_type: string[] | null;
}
/**
* Same validation as `assertBrowserSession`, but returns the resolved
* PB user record instead of discarding it — for routes that need to
* scope queries to "whoever is logged in" (e.g. the rep dashboard).
*/
export async function getBrowserSessionUser(
req: NextRequest,
): Promise<BrowserSessionUser> {
const authorization = req.headers.get("authorization") || "";
const token = authorization.startsWith("Bearer ")
? authorization.slice(7)
: authorization;
if (!token) {
throw Object.assign(new Error("Not authenticated."), { status: 401 });
}
const pbUrl =
process.env.NEXT_PUBLIC_POCKETBASE_URL || "http://127.0.0.1:8090";
const response = await fetch(
`${pbUrl.replace(/\/$/, "")}/api/collections/users/auth-refresh`,
{
method: "POST",
headers: { Authorization: token },
signal: AbortSignal.timeout(5_000),
},
);
if (!response.ok) {
throw Object.assign(new Error("Invalid or expired session."), {
status: 401,
});
}
const body = (await response.json()) as {
record?: { id?: string; user_type?: string[] | null };
};
const id = body.record?.id;
if (!id) {
throw Object.assign(new Error("Invalid or expired session."), {
status: 401,
});
}
return { id, user_type: body.record?.user_type ?? null };
}