wcz-layout
Version:
72 lines (71 loc) • 3.59 kB
JavaScript
import { n as getProviderByIssuer } from "./providers-Dbl95AxR.mjs";
import { f as getSessionUser, i as buildUser, s as hasPermission } from "./utils-KaM5VMTJ.mjs";
import { z as z$1 } from "zod";
import { createCsrfMiddleware, createMiddleware } from "@tanstack/react-start";
import * as jose from "jose";
//#region src/middleware/authMiddleware.ts
/**
* Verifies a Bearer JWT from a sibling service. The unverified `iss` only selects
* which provider to check against — the real check pins that provider's JWKS,
* issuer and (where the provider has one) audience.
*/
async function verifyBearer(token) {
const provider = getProviderByIssuer(jose.decodeJwt(token).iss);
if (!provider) throw new Error("Unauthorized: Unknown token issuer");
const { payload } = await jose.jwtVerify(token, provider.jwks, {
issuer: provider.issuer,
audience: provider.audience
});
return buildUser(payload, provider.id);
}
async function resolveUser(request) {
const authHeader = request.headers.get("Authorization");
if (authHeader?.startsWith("Bearer ")) try {
return await verifyBearer(authHeader.substring(7));
} catch (error) {
throw Response.json({ message: error instanceof Error ? error.message : "Unauthorized: Invalid access token" }, { status: 401 });
}
return getSessionUser();
}
/**
* Puts the caller in `context.user` — Bearer or cookie, `null` when anonymous —
* without requiring one. Deliberately a single instance, not a factory:
* `flattenMiddlewares` dedupes by object identity, so every middleware that
* declares this one as a dependency shares one resolution per request.
*/
const userMiddleware = createMiddleware().server(async ({ next, request }) => next({ context: { user: await resolveUser(request) } }));
/**
* Server-function guard, the counterpart to `requireAuth` on routes.
*
* - `authMiddleware()` — 401 unless a user is signed in (cookie or Bearer).
* - `authMiddleware("admin")` — also 403 unless they hold that permission.
*/
const authMiddleware = (permissionKey) => createMiddleware().middleware([userMiddleware]).server(async ({ next, context: { user } }) => {
if (!user) throw Response.json({ message: "Unauthorized: User not signed in" }, { status: 401 });
if (permissionKey && !hasPermission(user, permissionKey)) throw Response.json({ message: `Forbidden: User ${user.name} is not authorized to access this resource` }, { status: 403 });
return next({ context: { user } });
});
//#endregion
//#region src/middleware/validationMiddleware.ts
const validationMiddleware = (schema) => createMiddleware().server(async ({ next, request }) => {
const json = await request.json();
const result = schema.safeParse(json);
if (!result.success) {
const { fieldErrors } = z$1.flattenError(result.error);
const firstFieldName = Object.keys(fieldErrors)[0];
const firstErrorMessage = fieldErrors[firstFieldName]?.[0];
if (firstFieldName && firstErrorMessage) {
const name = firstFieldName.charAt(0).toUpperCase() + firstFieldName.slice(1);
const message = firstErrorMessage.replace(/^Invalid input:\s*/i, "").toLowerCase();
return Response.json({ message: `${name} - ${message}` }, { status: 400 });
}
return Response.json({ message: "Validation failed" }, { status: 400 });
}
return await next({ context: { data: result.data } });
});
//#endregion
//#region src/middleware/csrfMiddleware.ts
const csrfMiddleware = createCsrfMiddleware({ filter: (ctx) => ctx.handlerType === "serverFn" });
//#endregion
export { authMiddleware, csrfMiddleware, userMiddleware, validationMiddleware };
//# sourceMappingURL=middleware.mjs.map