Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,6 @@ type Handler = { default: { fetch: (req: Request) => Promise<Response> } };

const { default: handler } = (await import(SERVER_ENTRY.href)) as Handler;

if (!process.env.ADMIN_PANEL_METRICS_SECRET) {
console.warn('[metrics] ADMIN_PANEL_METRICS_SECRET is not set — /metrics will return 401 for all requests');
}

async function buildStaticRoutes(): Promise<Record<string, () => Response>> {
const routes: Record<string, () => Response> = {};
for await (const path of new Glob('**/*').scan(CLIENT_DIR)) {
Expand All @@ -51,7 +47,7 @@ async function buildStaticRoutes(): Promise<Record<string, () => Response>> {
return routes;
}

Bun.serve({
const server = Bun.serve({
port: Number(process.env.PORT ?? 3000),
routes: {
...(await buildStaticRoutes()),
Expand All @@ -72,4 +68,8 @@ Bun.serve({
},
});

console.log(`Admin panel listening on http://localhost:${process.env.PORT ?? 3000}`);
console.log(`Admin panel listening on http://localhost:${server.port}`);

if (!process.env.ADMIN_PANEL_METRICS_SECRET) {
console.warn('[metrics] ADMIN_PANEL_METRICS_SECRET is not set — /metrics will return 401 for all requests');
}
45 changes: 32 additions & 13 deletions src/server/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,26 @@ function extractCookieValue(response: Response, name: string): string | undefine
return undefined;
}

function getRequestOrigin(): string | undefined {
const origin = getRequestHeader('origin');
if (origin) return origin;

const referer = getRequestHeader('referer');
if (referer) {
try {
return new URL(referer).origin;
} catch {
return undefined;
}
}

const host = getRequestHeader('host');
if (!host) return undefined;

const proto = getRequestHeader('x-forwarded-proto') ?? 'http';
return `${proto}://${host}`;
}

export const adminLoginFn = createServerFn({ method: 'POST' })
.inputValidator(
z.object({
Expand Down Expand Up @@ -303,12 +323,17 @@ export const openIdCheckOptions = queryOptions({
});

export const checkOpenIdFn = createServerFn({ method: 'GET' }).handler(async () => {
const checkUrl = `${getServerApiUrl()}/api/admin/oauth/openid/check`;
try {
const response = await fetch(`${getServerApiUrl()}/api/admin/oauth/openid/check`);
if (!response.ok) return { available: false, ssoOnly: false };
const response = await fetch(checkUrl);
if (!response.ok) {
console.warn('[checkOpenIdFn] OpenID check failed:', response.status, checkUrl);
return { available: false, ssoOnly: false };
}
const ssoOnly = process.env.ADMIN_SSO_ONLY === 'true';
return { available: true, ssoOnly };
} catch {
} catch (error) {
console.warn('[checkOpenIdFn] OpenID check request failed:', checkUrl, error);
return { available: false, ssoOnly: false };
}
});
Expand All @@ -317,11 +342,12 @@ export const openidLoginFn = createServerFn({ method: 'GET' }).handler(async ()
try {
const baseUrl = getApiBaseUrl();
const authUrl = new URL(`${baseUrl}/api/admin/oauth/openid`);
const requestOrigin = getRequestOrigin();

/** Generate PKCE code_verifier and store in session */
const codeVerifier = crypto.randomBytes(32).toString('hex');
const codeChallenge = crypto.createHash('sha256').update(codeVerifier).digest('hex');
authUrl.searchParams.set('code_challenge', codeChallenge);
if (requestOrigin) authUrl.searchParams.set('redirect_uri', `${requestOrigin}/auth/openid/callback`);

const session = await useAppSession();
await session.update({ codeVerifier });
Expand All @@ -340,16 +366,9 @@ export const oauthExchangeFn = createServerFn({ method: 'POST' })
.handler(async ({ data }) => {
try {
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
const rawOrigin = getRequestHeader('origin') || getRequestHeader('referer');
if (rawOrigin) {
try {
headers['Origin'] = new URL(rawOrigin).origin;
} catch {
// malformed URL – skip forwarding
}
}
const requestOrigin = getRequestOrigin();
if (requestOrigin) headers['Origin'] = requestOrigin;

/** Read PKCE code_verifier from session (stored during openidLoginFn) */
const session = await useAppSession();
const { codeVerifier } = session.data;

Expand Down