mirror of
https://github.com/open-reception/appointment-booking-software.git
synced 2026-09-20 16:07:40 +02:00
Subdomain handling in login
This commit is contained in:
@@ -7,6 +7,8 @@ import { verifyPassphrase } from "$lib/server/utils/passphrase";
|
||||
import type { RequestHandler } from "./$types";
|
||||
import { registerOpenAPIRoute } from "$lib/server/openapi";
|
||||
import { UniversalLogger } from "$lib/logger";
|
||||
import { TenantAdminService } from "$lib/server/services/tenant-admin-service";
|
||||
import { env } from "$env/dynamic/private";
|
||||
|
||||
const logger = new UniversalLogger().setContext("AuthLoginAPI");
|
||||
|
||||
@@ -123,7 +125,7 @@ registerOpenAPIRoute("/auth/login", "POST", {
|
||||
}
|
||||
});
|
||||
|
||||
export const POST: RequestHandler = async ({ request, cookies, getClientAddress }) => {
|
||||
export const POST: RequestHandler = async ({ request, cookies, getClientAddress, url }) => {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const ipAddress = getClientAddress();
|
||||
@@ -154,6 +156,61 @@ export const POST: RequestHandler = async ({ request, cookies, getClientAddress
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Production-only: Validate subdomain for non-global admins
|
||||
if (env.NODE_ENV === "production") {
|
||||
const hostname = url.hostname;
|
||||
const hostParts = hostname.split(".");
|
||||
const hasSubdomain = hostParts.length > 2;
|
||||
const subdomain = hasSubdomain ? hostParts[0] : null;
|
||||
|
||||
// Global admins can login from anywhere
|
||||
if (user.role !== "GLOBAL_ADMIN") {
|
||||
if (!hasSubdomain || !subdomain) {
|
||||
logger.warn("Login attempt from non-subdomain by non-global admin", {
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
hostname,
|
||||
role: user.role
|
||||
});
|
||||
return json({ error: "Unknown user does for tenant" }, { status: 401 });
|
||||
}
|
||||
|
||||
if (user.tenantId) {
|
||||
try {
|
||||
const tenantService = await TenantAdminService.getTenantById(user.tenantId);
|
||||
const tenant = tenantService.tenantData;
|
||||
|
||||
if (!tenant || tenant.shortName !== subdomain) {
|
||||
logger.warn("Login attempt from wrong subdomain", {
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
expectedSubdomain: tenant?.shortName,
|
||||
actualSubdomain: subdomain,
|
||||
tenantId: user.tenantId
|
||||
});
|
||||
return json({ error: "Unknown user does for tenant" }, { status: 401 });
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("Failed to validate tenant for subdomain login", {
|
||||
userId: user.id,
|
||||
tenantId: user.tenantId,
|
||||
subdomain,
|
||||
error: String(error)
|
||||
});
|
||||
return json({ error: "Unknown user does for tenant" }, { status: 401 });
|
||||
}
|
||||
} else {
|
||||
logger.warn("Login attempt from subdomain by user without matching tenant", {
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
subdomain,
|
||||
role: user.role
|
||||
});
|
||||
return json({ error: "Unknown user does for tenant" }, { status: 401 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle passphrase authentication
|
||||
if (body.passphrase) {
|
||||
if (!user.passphraseHash) {
|
||||
|
||||
Reference in New Issue
Block a user