refactor: enhance forward authentication UI and API integration

- Updated the alert block in the HandleForwardAuth component to provide clearer requirements for deploying the authentication proxy.
- Added a DnsHelperModal to assist with DNS configuration in the ForwardAuthServers component.
- Refined API input schemas for forward authentication operations to improve type safety and clarity.
- Removed the obsolete forward-auth SSO design document to streamline documentation.

These changes improve the user experience and maintainability of the forward authentication feature across the application.
This commit is contained in:
Mauricio Siu
2026-06-06 13:27:17 -06:00
parent 28673a6166
commit 51b5af55d0
6 changed files with 84 additions and 397 deletions
@@ -97,12 +97,28 @@ export const HandleForwardAuth = ({ domainId, applicationId }: Props) => {
</DialogDescription>
</DialogHeader>
<AlertBlock type="info">
The authentication proxy must be deployed for this app's server in SSO
settings. The domain must share its base domain.
<AlertBlock type="warning">
<div className="flex flex-col gap-1">
<span className="font-medium">Requirements</span>
<ol className="list-decimal pl-4 text-sm">
<li>
The authentication proxy container must be deployed and running
on this app's server. Configure it under{" "}
<span className="font-medium">
Settings SSO Application Authentication
</span>
.
</li>
<li>
This domain must share the same base domain as the
authentication domain (e.g. <code>app.acme.com</code> and{" "}
<code>auth.acme.com</code>).
</li>
</ol>
</div>
</AlertBlock>
<div className="flex items-center justify-between rounded-lg border p-4">
<div className="flex items-center justify-between rounded-lg border p-4 mt-2">
<div className="flex flex-col">
<span className="text-sm font-medium">
Protect this domain with SSO
@@ -10,6 +10,7 @@ import {
} from "lucide-react";
import { useEffect, useState } from "react";
import { toast } from "sonner";
import { DnsHelperModal } from "@/components/dashboard/application/domains/dns-helper-modal";
import { AlertBlock } from "@/components/shared/alert-block";
import { DialogAction } from "@/components/shared/dialog-action";
import { Badge } from "@/components/ui/badge";
@@ -61,6 +62,7 @@ export const ForwardAuthServers = () => {
return () => clearTimeout(id);
}, []);
const { data: hostIp } = api.settings.getIp.useQuery();
const { data: servers, isPending } = api.forwardAuth.serverStatus.useQuery(
undefined,
{ enabled, refetchOnWindowFocus: false, staleTime: 30_000 },
@@ -236,6 +238,10 @@ export const ForwardAuthServers = () => {
domain (e.g. auth.acme.com) per server, register its callback URL once
in your identity provider, then deploy the proxy. Apps on that server
under the same base domain are then one click to protect.
<span className="mt-2 block font-medium">
Only OIDC providers are supported SAML is not compatible with the
forward-auth proxy.
</span>
</CardDescription>
</CardHeader>
<CardContent>
@@ -289,6 +295,17 @@ export const ForwardAuthServers = () => {
}
className="font-mono text-sm"
/>
{f?.host && !f.host.includes("sslip.io") && (
<DnsHelperModal
domain={{
host: f.host,
https: f.https,
}}
serverIp={
srv.ipAddress ?? hostIp?.toString() ?? undefined
}
/>
)}
<Button
type="button"
variant="secondary"
@@ -13,14 +13,18 @@ import {
removeForwardAuthSettings,
setForwardAuthSettings,
} from "@dokploy/server";
import { apiSetForwardAuthSettings } from "@dokploy/server/db/schema";
import { z } from "zod";
import {
apiDeployForwardAuthOnServer,
apiForwardAuthDomainTarget,
apiForwardAuthServerTarget,
apiSetForwardAuthSettings,
} from "@dokploy/server/db/schema";
import { createTRPCRouter, enterpriseProcedure } from "@/server/api/trpc";
import { audit } from "@/server/api/utils/audit";
export const forwardAuthRouter = createTRPCRouter({
getAuthDomain: enterpriseProcedure
.input(z.object({ serverId: z.string().nullable() }))
.input(apiForwardAuthServerTarget)
.query(async ({ input }) => {
const settings = await getForwardAuthSettings(input.serverId);
if (!settings) return null;
@@ -58,7 +62,7 @@ export const forwardAuthRouter = createTRPCRouter({
}),
removeAuthDomain: enterpriseProcedure
.input(z.object({ serverId: z.string().nullable() }))
.input(apiForwardAuthServerTarget)
.mutation(async ({ ctx, input }) => {
if (input.serverId) await findServerById(input.serverId);
const result = await removeForwardAuthSettings(input.serverId);
@@ -83,12 +87,7 @@ export const forwardAuthRouter = createTRPCRouter({
),
deployOnServer: enterpriseProcedure
.input(
z.object({
serverId: z.string().nullable(),
providerId: z.string().min(1),
}),
)
.input(apiDeployForwardAuthOnServer)
.mutation(async ({ ctx, input }) => {
if (input.serverId) await findServerById(input.serverId);
const result = await deployForwardAuthOnServer({
@@ -106,7 +105,7 @@ export const forwardAuthRouter = createTRPCRouter({
}),
removeOnServer: enterpriseProcedure
.input(z.object({ serverId: z.string().nullable() }))
.input(apiForwardAuthServerTarget)
.mutation(async ({ ctx, input }) => {
if (input.serverId) await findServerById(input.serverId);
const result = await removeForwardAuthProxy(input.serverId);
@@ -120,11 +119,11 @@ export const forwardAuthRouter = createTRPCRouter({
}),
status: enterpriseProcedure
.input(z.object({ domainId: z.string().min(1) }))
.input(apiForwardAuthDomainTarget)
.query(({ ctx, input }) => getDomainSsoStatus(ctx, input.domainId)),
enable: enterpriseProcedure
.input(z.object({ domainId: z.string().min(1) }))
.input(apiForwardAuthDomainTarget)
.mutation(async ({ ctx, input }) => {
const domain = await assertApplicationDomainAccess(
ctx,
@@ -144,7 +143,7 @@ export const forwardAuthRouter = createTRPCRouter({
}),
disable: enterpriseProcedure
.input(z.object({ domainId: z.string().min(1) }))
.input(apiForwardAuthDomainTarget)
.mutation(async ({ ctx, input }) => {
const domain = await assertApplicationDomainAccess(
ctx,