72 lines
2.0 KiB
TypeScript
72 lines
2.0 KiB
TypeScript
import type { SelfHostedIdentityConfig } from "./config.ts";
|
|
|
|
export type IdentityRequestHandler = (
|
|
request: Request,
|
|
) => Response | Promise<Response>;
|
|
|
|
export interface IdentityAuthHandlers {
|
|
GET: IdentityRequestHandler;
|
|
POST: IdentityRequestHandler;
|
|
}
|
|
|
|
export function normalizeIdentityHost(value: string | null): string | null {
|
|
if (!value || value !== value.trim() || /[\s,@/\\]/.test(value)) return null;
|
|
|
|
try {
|
|
const url = new URL(`https://${value}`);
|
|
if (
|
|
url.username ||
|
|
url.password ||
|
|
url.pathname !== "/" ||
|
|
url.search ||
|
|
url.hash
|
|
) {
|
|
return null;
|
|
}
|
|
return url.host.toLowerCase();
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function resolveIdentitySurface(
|
|
hostHeader: string | null,
|
|
config: SelfHostedIdentityConfig,
|
|
): "user" | "admin" | null {
|
|
const host = normalizeIdentityHost(hostHeader);
|
|
if (!host) return null;
|
|
|
|
if (host === new URL(config.userOrigin).host.toLowerCase()) return "user";
|
|
if (host === new URL(config.adminOrigin).host.toLowerCase()) return "admin";
|
|
return null;
|
|
}
|
|
|
|
function isAdminEndpoint(request: Request): boolean {
|
|
try {
|
|
const path = decodeURIComponent(new URL(request.url).pathname);
|
|
return /^\/api\/auth\/+admin(?:\/|$)/i.test(path);
|
|
} catch {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
export function createHostIsolatedAuthHandlers(
|
|
config: SelfHostedIdentityConfig,
|
|
handlers: { user: IdentityAuthHandlers },
|
|
): IdentityAuthHandlers {
|
|
const dispatch =
|
|
(method: keyof IdentityAuthHandlers): IdentityRequestHandler =>
|
|
async (request) => {
|
|
const surface = resolveIdentitySurface(request.headers.get("host"), config);
|
|
if (!surface) {
|
|
return new Response("Unrecognized identity host", { status: 421 });
|
|
}
|
|
if (surface === "user" && isAdminEndpoint(request)) {
|
|
return new Response("Not found", { status: 404 });
|
|
}
|
|
return handlers.user[method](request);
|
|
};
|
|
|
|
return { GET: dispatch("GET"), POST: dispatch("POST") };
|
|
}
|