59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
import type { MiddlewareHandler } from "astro";
|
|
import { getOptions } from "./runtime.js";
|
|
import { verifyAndDecode } from "./lib/sign.js";
|
|
|
|
function parseCookies(cookieHeader: string | null): Record<string, string> {
|
|
const out: Record<string, string> = {};
|
|
if (!cookieHeader) return out;
|
|
const parts = cookieHeader.split(/;\s*/);
|
|
for (const part of parts) {
|
|
const idx = part.indexOf("=");
|
|
if (idx === -1) continue;
|
|
const name = decodeURIComponent(part.slice(0, idx));
|
|
const val = decodeURIComponent(part.slice(idx + 1));
|
|
out[name] = val;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function escapeRegex(str: string): string {
|
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
}
|
|
|
|
function globToRegExp(pattern: string): RegExp {
|
|
const escaped = pattern.split("*").map(escapeRegex).join(".*");
|
|
return new RegExp(`^${escaped}$`);
|
|
}
|
|
|
|
function isProtected(pathname: string, patterns: string[]): boolean {
|
|
return patterns.some((p) => globToRegExp(p).test(pathname));
|
|
}
|
|
|
|
export const onRequest: MiddlewareHandler = async (context, next) => {
|
|
const { request, locals, url } = context;
|
|
const options = getOptions();
|
|
const cookieName = options.cookie.name;
|
|
const cookies = parseCookies(request.headers.get("cookie"));
|
|
const token = cookies[cookieName];
|
|
|
|
locals.user = null;
|
|
if (token) {
|
|
const res = await verifyAndDecode<{ sub: string; email?: string }>(
|
|
token,
|
|
options.cookie.signingSecret,
|
|
);
|
|
if (res.valid && res.payload && typeof res.payload.sub === "string") {
|
|
locals.user = { sub: res.payload.sub, email: res.payload.email };
|
|
}
|
|
}
|
|
|
|
if (isProtected(url.pathname, options.protected) && !locals.user) {
|
|
const returnTo = url.pathname + (url.search || "");
|
|
const loginUrl = new URL(options.routes.login, url);
|
|
loginUrl.searchParams.set("return_to", returnTo);
|
|
return Response.redirect(loginUrl, 302);
|
|
}
|
|
|
|
return next();
|
|
};
|