refactor: update forward authentication handling in domain schema and tests

- Replaced `forwardAuthProviderId` with `forwardAuthEnabled` in the domain schema to simplify the configuration of forward authentication.
- Updated related tests to reflect this change, ensuring consistency across the application.
- Introduced a new SQL migration to create the `forward_auth_settings` table for managing authentication domains and their configurations.

This refactor enhances the clarity and maintainability of the forward authentication logic within the application.
This commit is contained in:
Mauricio Siu
2026-06-06 03:53:45 -06:00
parent 35f452d25f
commit 1df6774ee8
12 changed files with 8483 additions and 29 deletions
@@ -34,7 +34,7 @@ describe("Host rule format regression tests", () => {
stripPath: false, stripPath: false,
customEntrypoint: null, customEntrypoint: null,
middlewares: null, middlewares: null,
forwardAuthProviderId: null, forwardAuthEnabled: false,
}; };
describe("Host rule format validation", () => { describe("Host rule format validation", () => {
@@ -23,7 +23,7 @@ describe("createDomainLabels", () => {
internalPath: "/", internalPath: "/",
stripPath: false, stripPath: false,
middlewares: null, middlewares: null,
forwardAuthProviderId: null, forwardAuthEnabled: false,
}; };
it("should create basic labels for web entrypoint", async () => { it("should create basic labels for web entrypoint", async () => {
@@ -34,7 +34,7 @@ const baseDomain: Domain = {
internalPath: "/", internalPath: "/",
stripPath: false, stripPath: false,
middlewares: null, middlewares: null,
forwardAuthProviderId: null, forwardAuthEnabled: false,
}; };
describe("forwardAuthMiddlewareName", () => { describe("forwardAuthMiddlewareName", () => {
@@ -62,7 +62,7 @@ describe("createRouterConfig forward-auth wiring", () => {
test("adds forward-auth middleware when a provider is linked", async () => { test("adds forward-auth middleware when a provider is linked", async () => {
const domain: Domain = { const domain: Domain = {
...baseDomain, ...baseDomain,
forwardAuthProviderId: "provider-abc", forwardAuthEnabled: true,
}; };
const config = await createRouterConfig(app, domain, "websecure"); const config = await createRouterConfig(app, domain, "websecure");
expect(config.middlewares).toContain( expect(config.middlewares).toContain(
@@ -73,7 +73,7 @@ describe("createRouterConfig forward-auth wiring", () => {
test("forward-auth runs before custom domain middlewares", async () => { test("forward-auth runs before custom domain middlewares", async () => {
const domain: Domain = { const domain: Domain = {
...baseDomain, ...baseDomain,
forwardAuthProviderId: "provider-abc", forwardAuthEnabled: true,
middlewares: ["rate-limit@file"], middlewares: ["rate-limit@file"],
}; };
const config = await createRouterConfig(app, domain, "websecure"); const config = await createRouterConfig(app, domain, "websecure");
@@ -89,7 +89,7 @@ describe("createRouterConfig forward-auth wiring", () => {
const domain: Domain = { const domain: Domain = {
...baseDomain, ...baseDomain,
https: true, https: true,
forwardAuthProviderId: "provider-abc", forwardAuthEnabled: true,
}; };
const config = await createRouterConfig(app, domain, "web"); const config = await createRouterConfig(app, domain, "web");
expect(config.middlewares).toContain("redirect-to-https"); expect(config.middlewares).toContain("redirect-to-https");
@@ -148,7 +148,7 @@ const baseDomain: Domain = {
internalPath: "/", internalPath: "/",
stripPath: false, stripPath: false,
middlewares: null, middlewares: null,
forwardAuthProviderId: null, forwardAuthEnabled: false,
}; };
const baseRedirect: Redirect = { const baseRedirect: Redirect = {
@@ -0,0 +1,16 @@
CREATE TABLE "forward_auth_settings" (
"forwardAuthSettingsId" text PRIMARY KEY NOT NULL,
"authDomain" text NOT NULL,
"baseDomain" text NOT NULL,
"https" boolean DEFAULT true NOT NULL,
"certificateType" "certificateType" DEFAULT 'letsencrypt' NOT NULL,
"customCertResolver" text,
"providerId" text,
"serverId" text,
"createdAt" text NOT NULL,
CONSTRAINT "forward_auth_settings_serverId_unique" UNIQUE("serverId")
);
--> statement-breakpoint
ALTER TABLE "domain" ADD COLUMN "forwardAuthEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE "forward_auth_settings" ADD CONSTRAINT "forward_auth_settings_providerId_sso_provider_provider_id_fk" FOREIGN KEY ("providerId") REFERENCES "public"."sso_provider"("provider_id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "forward_auth_settings" ADD CONSTRAINT "forward_auth_settings_serverId_server_serverId_fk" FOREIGN KEY ("serverId") REFERENCES "public"."server"("serverId") ON DELETE cascade ON UPDATE no action;
+1 -1
View File
@@ -8329,4 +8329,4 @@
"schemas": {}, "schemas": {},
"tables": {} "tables": {}
} }
} }
File diff suppressed because it is too large Load Diff
+7
View File
@@ -1191,6 +1191,13 @@
"when": 1780127552074, "when": 1780127552074,
"tag": "0169_parched_johnny_storm", "tag": "0169_parched_johnny_storm",
"breakpoints": true "breakpoints": true
},
{
"idx": 170,
"version": "7",
"when": 1780739532982,
"tag": "0170_amusing_spot",
"breakpoints": true
} }
] ]
} }
+3 -11
View File
@@ -16,7 +16,6 @@ import { applications } from "./application";
import { compose } from "./compose"; import { compose } from "./compose";
import { previewDeployments } from "./preview-deployments"; import { previewDeployments } from "./preview-deployments";
import { certificateType } from "./shared"; import { certificateType } from "./shared";
import { ssoProvider } from "./sso";
export const domainType = pgEnum("domainType", [ export const domainType = pgEnum("domainType", [
"compose", "compose",
@@ -56,10 +55,7 @@ export const domains = pgTable("domain", {
internalPath: text("internalPath").default("/"), internalPath: text("internalPath").default("/"),
stripPath: boolean("stripPath").notNull().default(false), stripPath: boolean("stripPath").notNull().default(false),
middlewares: text("middlewares").array().default(sql`ARRAY[]::text[]`), middlewares: text("middlewares").array().default(sql`ARRAY[]::text[]`),
forwardAuthProviderId: text("forwardAuthProviderId").references( forwardAuthEnabled: boolean("forwardAuthEnabled").notNull().default(false),
() => ssoProvider.providerId,
{ onDelete: "set null" },
),
}); });
export const domainsRelations = relations(domains, ({ one }) => ({ export const domainsRelations = relations(domains, ({ one }) => ({
@@ -75,10 +71,6 @@ export const domainsRelations = relations(domains, ({ one }) => ({
fields: [domains.previewDeploymentId], fields: [domains.previewDeploymentId],
references: [previewDeployments.previewDeploymentId], references: [previewDeployments.previewDeploymentId],
}), }),
forwardAuthProvider: one(ssoProvider, {
fields: [domains.forwardAuthProviderId],
references: [ssoProvider.providerId],
}),
})); }));
const createSchema = createInsertSchema(domains, { const createSchema = createInsertSchema(domains, {
@@ -103,7 +95,7 @@ export const apiCreateDomain = createSchema.pick({
internalPath: true, internalPath: true,
stripPath: true, stripPath: true,
middlewares: true, middlewares: true,
forwardAuthProviderId: true, forwardAuthEnabled: true,
}); });
export const apiFindDomain = z.object({ export const apiFindDomain = z.object({
@@ -136,6 +128,6 @@ export const apiUpdateDomain = createSchema
internalPath: true, internalPath: true,
stripPath: true, stripPath: true,
middlewares: true, middlewares: true,
forwardAuthProviderId: true, forwardAuthEnabled: true,
}) })
.merge(createSchema.pick({ domainId: true }).required()); .merge(createSchema.pick({ domainId: true }).required());
@@ -108,10 +108,7 @@ export const getDomainSsoStatus = async (
domain: ["read"], domain: ["read"],
}); });
} }
return { return { enabled: !!domain.forwardAuthEnabled };
enabled: !!domain.forwardAuthProviderId,
providerId: domain.forwardAuthProviderId ?? null,
};
}; };
const settingsWhere = (serverId: string | null) => const settingsWhere = (serverId: string | null) =>
@@ -348,9 +345,7 @@ export const enableForwardAuthOnDomain = async (input: {
}); });
} }
await updateDomainById(input.domainId, { await updateDomainById(input.domainId, { forwardAuthEnabled: true });
forwardAuthProviderId: settings.providerId,
});
const domain = await findDomainById(input.domainId); const domain = await findDomainById(input.domainId);
await manageDomain(application, domain); await manageDomain(application, domain);
@@ -365,7 +360,7 @@ export const disableForwardAuthOnDomain = async (input: {
); );
const uniqueConfigKey = domain.uniqueConfigKey; const uniqueConfigKey = domain.uniqueConfigKey;
await updateDomainById(input.domainId, { forwardAuthProviderId: null }); await updateDomainById(input.domainId, { forwardAuthEnabled: false });
const updated = await findDomainById(input.domainId); const updated = await findDomainById(input.domainId);
await manageDomain(application, updated); await manageDomain(application, updated);
await removeForwardAuthMiddleware(application, uniqueConfigKey); await removeForwardAuthMiddleware(application, uniqueConfigKey);
+1 -1
View File
@@ -198,7 +198,7 @@ export const createRouterConfig = async (
// authentication runs first. No-op unless the domain links a provider. // authentication runs first. No-op unless the domain links a provider.
// The -errors middleware must come first so a 401 from the auth check is // The -errors middleware must come first so a 401 from the auth check is
// rewritten to a 302 redirect to the login page. // rewritten to a 302 redirect to the login page.
if (domain.forwardAuthProviderId) { if (domain.forwardAuthEnabled) {
const name = forwardAuthMiddlewareName(appName, uniqueConfigKey); const name = forwardAuthMiddlewareName(appName, uniqueConfigKey);
routerConfig.middlewares?.push(`${name}-errors`); routerConfig.middlewares?.push(`${name}-errors`);
routerConfig.middlewares?.push(name); routerConfig.middlewares?.push(name);
@@ -73,7 +73,7 @@ export const createForwardAuthMiddleware = async (
app: ApplicationNested, app: ApplicationNested,
domain: Domain, domain: Domain,
) => { ) => {
if (!domain.forwardAuthProviderId) { if (!domain.forwardAuthEnabled) {
return; return;
} }