mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-15 20:25:23 +02:00
feat(sso): refactor SSO provider update logic
- Changed the update mechanism for SSO providers to use a new `updateSSOProvider` function, improving code clarity and maintainability. - Updated the payload structure for OIDC and SAML configurations to directly use the input values instead of stringifying them. - Enhanced the overall handling of SSO provider updates within the API router.
This commit is contained in:
@@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Plus, Trash2 } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import type { FieldArrayPath } from "react-hook-form";
|
||||
import { useFieldArray, useForm } from "react-hook-form";
|
||||
import { useFieldArray, useForm, useWatch } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
} from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { api } from "@/utils/api";
|
||||
import { useUrl } from "@/utils/hooks/use-url";
|
||||
|
||||
const DEFAULT_SCOPES = ["openid", "email", "profile"];
|
||||
|
||||
@@ -120,6 +121,14 @@ export function RegisterOidcDialog({
|
||||
defaultValues: formDefaultValues,
|
||||
});
|
||||
|
||||
const watchedProviderId = useWatch({
|
||||
control: form.control,
|
||||
name: "providerId",
|
||||
defaultValue: "",
|
||||
});
|
||||
|
||||
const baseURL = useUrl();
|
||||
|
||||
useEffect(() => {
|
||||
if (!data || !open) return;
|
||||
const domains = data.domain
|
||||
@@ -242,6 +251,17 @@ export function RegisterOidcDialog({
|
||||
Unique identifier; used in callback URL path.
|
||||
{isEdit && " Cannot be changed when editing."}
|
||||
</FormDescription>
|
||||
{baseURL && (
|
||||
<div className="rounded-md bg-muted px-3 py-2 text-xs">
|
||||
<p className="font-medium text-muted-foreground">
|
||||
Callback URL (configure in your IdP)
|
||||
</p>
|
||||
<p className="mt-0.5 break-all font-mono">
|
||||
{baseURL}/api/auth/sso/callback/
|
||||
{watchedProviderId?.trim() || "..."}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Plus, Trash2 } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { type FieldArrayPath, useFieldArray, useForm } from "react-hook-form";
|
||||
import {
|
||||
type FieldArrayPath,
|
||||
useFieldArray,
|
||||
useForm,
|
||||
useWatch,
|
||||
} from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -28,6 +33,7 @@ import {
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { api } from "@/utils/api";
|
||||
import { useUrl } from "@/utils/hooks/use-url";
|
||||
|
||||
const domainsArraySchema = z
|
||||
.array(z.string().trim())
|
||||
@@ -115,13 +121,7 @@ export function RegisterSamlDialog({
|
||||
? updateMutation.isLoading
|
||||
: registerMutation.isLoading;
|
||||
|
||||
const [baseURL, setBaseURL] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
setBaseURL(window.location.origin);
|
||||
}
|
||||
}, []);
|
||||
const baseURL = useUrl();
|
||||
|
||||
const form = useForm<SamlProviderForm>({
|
||||
resolver: zodResolver(samlProviderSchema),
|
||||
@@ -148,6 +148,12 @@ export function RegisterSamlDialog({
|
||||
});
|
||||
}, [data, open, form]);
|
||||
|
||||
const watchedProviderId = useWatch({
|
||||
control: form.control,
|
||||
name: "providerId",
|
||||
defaultValue: "",
|
||||
});
|
||||
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
control: form.control,
|
||||
name: "domains" as FieldArrayPath<SamlProviderForm>,
|
||||
@@ -243,6 +249,17 @@ export function RegisterSamlDialog({
|
||||
Cannot be changed when editing.
|
||||
</FormDescription>
|
||||
)}
|
||||
{baseURL && (
|
||||
<div className="rounded-md bg-muted px-3 py-2 text-xs">
|
||||
<p className="font-medium text-muted-foreground">
|
||||
Callback URL (configure in your IdP)
|
||||
</p>
|
||||
<p className="mt-0.5 break-all font-mono">
|
||||
{baseURL}/api/auth/sso/saml2/callback/
|
||||
{watchedProviderId?.trim() || "..."}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Shield,
|
||||
Trash2,
|
||||
} from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { DialogAction } from "@/components/shared/dialog-action";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { api } from "@/utils/api";
|
||||
import { useUrl } from "@/utils/hooks/use-url";
|
||||
import { RegisterOidcDialog } from "./register-oidc-dialog";
|
||||
import { RegisterSamlDialog } from "./register-saml-dialog";
|
||||
|
||||
@@ -76,18 +77,12 @@ export const SSOSettings = () => {
|
||||
const utils = api.useUtils();
|
||||
const [detailsProvider, setDetailsProvider] =
|
||||
useState<ProviderForDetails | null>(null);
|
||||
const [baseURL, setBaseURL] = useState("");
|
||||
const baseURL = useUrl();
|
||||
const [manageOriginsOpen, setManageOriginsOpen] = useState(false);
|
||||
const [editingOrigin, setEditingOrigin] = useState<string | null>(null);
|
||||
const [editingValue, setEditingValue] = useState("");
|
||||
const [newOriginInput, setNewOriginInput] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
setBaseURL(window.location.origin);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const { data: providers, isLoading } = api.sso.listProviders.useQuery();
|
||||
const { data: userData } = api.user.get.useQuery(undefined, {
|
||||
enabled: manageOriginsOpen,
|
||||
|
||||
@@ -154,33 +154,27 @@ export const ssoRouter = createTRPCRouter({
|
||||
}
|
||||
|
||||
const domain = input.domains.join(",");
|
||||
|
||||
const updatePayload: {
|
||||
const updateBody: {
|
||||
issuer: string;
|
||||
domain: string;
|
||||
oidcConfig?: string;
|
||||
samlConfig?: string;
|
||||
oidcConfig?: (typeof input)["oidcConfig"];
|
||||
samlConfig?: (typeof input)["samlConfig"];
|
||||
} = {
|
||||
issuer: input.issuer,
|
||||
domain,
|
||||
};
|
||||
if (input.oidcConfig != null) {
|
||||
updatePayload.oidcConfig = JSON.stringify(input.oidcConfig);
|
||||
updateBody.oidcConfig = input.oidcConfig;
|
||||
}
|
||||
if (input.samlConfig != null) {
|
||||
updatePayload.samlConfig = JSON.stringify(input.samlConfig);
|
||||
updateBody.samlConfig = input.samlConfig;
|
||||
}
|
||||
|
||||
await db
|
||||
.update(ssoProvider)
|
||||
.set(updatePayload)
|
||||
.where(
|
||||
and(
|
||||
eq(ssoProvider.providerId, input.providerId),
|
||||
eq(ssoProvider.organizationId, ctx.session.activeOrganizationId),
|
||||
eq(ssoProvider.userId, ctx.session.userId),
|
||||
),
|
||||
);
|
||||
await auth.updateSSOProvider({
|
||||
params: { providerId: input.providerId },
|
||||
body: updateBody,
|
||||
headers: requestToHeaders(ctx.req),
|
||||
});
|
||||
return { success: true };
|
||||
}),
|
||||
deleteProvider: enterpriseProcedure
|
||||
|
||||
@@ -349,6 +349,7 @@ export const auth = {
|
||||
handler,
|
||||
createApiKey: api.createApiKey,
|
||||
registerSSOProvider: api.registerSSOProvider,
|
||||
updateSSOProvider: api.updateSSOProvider,
|
||||
};
|
||||
|
||||
export const validateRequest = async (request: IncomingMessage) => {
|
||||
|
||||
Reference in New Issue
Block a user