fix(dashboard): proxy API auth on custom domains
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { getSessionData } from '$lib/utils/services/auth/session';
|
||||
import { getHasCioCookies } from '$lib/utils/functions/cookies';
|
||||
import { applyCspExtensions } from '$lib/utils/csp';
|
||||
import { proxyRequestToApi, shouldForwardToApi } from '$lib/utils/proxy-api-request';
|
||||
import { type Handle, type HandleServerError, redirect } from '@sveltejs/kit';
|
||||
import { isPublicApiRoute, isPublicRoute } from '$lib/utils/functions/routes/isPublicRoute';
|
||||
import { ROUTE } from '$lib/utils/constants/routes';
|
||||
@@ -35,6 +36,11 @@ function ensureAnalyticsSessionCookie(cookies: Parameters<Handle>[0]['event']['c
|
||||
|
||||
export const handle: Handle = async (args) => {
|
||||
const { event } = args;
|
||||
|
||||
if (shouldForwardToApi(event.url.pathname)) {
|
||||
return proxyRequestToApi(event.request);
|
||||
}
|
||||
|
||||
const sessionData = await getSessionData(event.cookies);
|
||||
|
||||
if (sessionData) {
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Mirrors `apps/tenant-router` path splitting for BYOD custom domains.
|
||||
*
|
||||
* Approximated forwards customer domains straight to the dashboard Render
|
||||
* service, so `/proxy/*` and `/api/auth/*` never reach the API unless we
|
||||
* forward them here. Tenant subdomains on `*.myclassroomio.com` hit the
|
||||
* Cloudflare Worker first and normally never exercise this path.
|
||||
*/
|
||||
|
||||
const PROXY_PREFIX = '/proxy';
|
||||
const AUTH_PREFIX = '/api/auth';
|
||||
|
||||
export function shouldForwardToApi(pathname: string): boolean {
|
||||
return (
|
||||
pathname === PROXY_PREFIX ||
|
||||
pathname.startsWith(`${PROXY_PREFIX}/`) ||
|
||||
pathname === AUTH_PREFIX ||
|
||||
pathname.startsWith(`${AUTH_PREFIX}/`)
|
||||
);
|
||||
}
|
||||
|
||||
function resolveApiUpstreamPath(pathname: string): string {
|
||||
if (pathname === PROXY_PREFIX || pathname.startsWith(`${PROXY_PREFIX}/`)) {
|
||||
return pathname.slice(PROXY_PREFIX.length) || '/';
|
||||
}
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
function resolveApiUpstreamBase(): string | null {
|
||||
const base = process.env.PRIVATE_SERVER_URL || process.env.PUBLIC_SERVER_URL;
|
||||
if (!base) return null;
|
||||
|
||||
return base.replace(/\/$/, '');
|
||||
}
|
||||
|
||||
function resolveOriginalHost(request: Request, url: URL): string {
|
||||
return request.headers.get('x-forwarded-host') ?? request.headers.get('host') ?? url.host;
|
||||
}
|
||||
|
||||
export async function proxyRequestToApi(request: Request): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
const apiBase = resolveApiUpstreamBase();
|
||||
|
||||
if (!apiBase) {
|
||||
console.error('proxyRequestToApi: missing PRIVATE_SERVER_URL / PUBLIC_SERVER_URL');
|
||||
return new Response('API upstream not configured', { status: 502 });
|
||||
}
|
||||
|
||||
const upstreamPath = resolveApiUpstreamPath(url.pathname);
|
||||
const upstreamUrl = new URL(`${upstreamPath}${url.search}`, apiBase);
|
||||
const originalHost = resolveOriginalHost(request, url);
|
||||
|
||||
const upstreamHeaders = new Headers(request.headers);
|
||||
upstreamHeaders.set('host', upstreamUrl.host);
|
||||
upstreamHeaders.set('x-forwarded-host', originalHost);
|
||||
upstreamHeaders.set('x-forwarded-proto', url.protocol.replace(':', ''));
|
||||
|
||||
const forwardedFor = request.headers.get('cf-connecting-ip') ?? request.headers.get('x-forwarded-for');
|
||||
if (forwardedFor) {
|
||||
upstreamHeaders.set('x-forwarded-for', forwardedFor);
|
||||
}
|
||||
|
||||
const init: RequestInit = {
|
||||
method: request.method,
|
||||
headers: upstreamHeaders,
|
||||
redirect: 'manual'
|
||||
};
|
||||
|
||||
if (request.method !== 'GET' && request.method !== 'HEAD') {
|
||||
init.body = await request.arrayBuffer();
|
||||
}
|
||||
|
||||
return fetch(upstreamUrl, init);
|
||||
}
|
||||
Reference in New Issue
Block a user