Files
famapp/src/lib/api-auth.ts
T

74 lines
1.9 KiB
TypeScript

import { eq } from "drizzle-orm";
import { auth } from "@/lib/auth";
import { db } from "@/lib/db";
import { resolveBearerToken } from "@/modules/_core/api-token";
import { householdMembers } from "@/modules/_core/schema";
export type ApiAuthContext = {
householdId: string;
userId: string | null;
role: "owner" | "member" | null;
};
function parseBearerToken(request: Request): string | null {
const header = request.headers.get("Authorization");
if (!header?.startsWith("Bearer ")) return null;
const token = header.slice("Bearer ".length).trim();
return token.length > 0 ? token : null;
}
async function resolveSessionAuth(): Promise<ApiAuthContext | null> {
let session;
try {
session = await auth();
} catch {
return null;
}
if (!session?.user?.id) return null;
const [row] = await db
.select({
householdId: householdMembers.householdId,
role: householdMembers.role,
})
.from(householdMembers)
.where(eq(householdMembers.userId, session.user.id))
.limit(1);
if (!row) return null;
return {
householdId: row.householdId,
userId: session.user.id,
role: row.role,
};
}
export async function resolveApiAuth(request: Request): Promise<ApiAuthContext | null> {
const sessionAuth = await resolveSessionAuth();
if (sessionAuth) return sessionAuth;
const rawToken = parseBearerToken(request);
if (!rawToken) return null;
const bearer = await resolveBearerToken(rawToken);
if (!bearer) return null;
return {
householdId: bearer.householdId,
userId: null,
role: null,
};
}
export async function requireApiAuth(request: Request): Promise<ApiAuthContext> {
const ctx = await resolveApiAuth(request);
if (!ctx) {
throw new Response(JSON.stringify({ error: "Unauthorized" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
return ctx;
}