"use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { Button } from "@app/components/ui/button"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@app/components/ui/form"; import { Input } from "@app/components/ui/input"; import { useForm } from "react-hook-form"; import { toast } from "@app/hooks/useToast"; import { useRouter, useParams, redirect } from "next/navigation"; import { SettingsContainer, SettingsSection, SettingsSectionHeader, SettingsSectionTitle, SettingsSectionDescription, SettingsSectionBody, SettingsSectionForm, SettingsSectionFooter, SettingsSectionGrid } from "@app/components/Settings"; import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { useState, useEffect } from "react"; import { SwitchInput } from "@app/components/SwitchInput"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { InfoIcon, ExternalLink } from "lucide-react"; import { InfoSection, InfoSectionContent, InfoSections, InfoSectionTitle } from "@app/components/InfoSection"; import CopyToClipboard from "@app/components/CopyToClipboard"; import { Badge } from "@app/components/ui/badge"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useTranslations } from "next-intl"; export default function GeneralPage() { const { env } = useEnvContext(); const api = createApiClient({ env }); const router = useRouter(); const { idpId } = useParams(); const [loading, setLoading] = useState(false); const [initialLoading, setInitialLoading] = useState(true); const { isUnlocked } = useLicenseStatusContext(); const redirectUrl = `${env.app.dashboardUrl}/auth/idp/${idpId}/oidc/callback`; const t = useTranslations(); const GeneralFormSchema = z.object({ name: z.string().min(2, { message: t('nameMin', {len: 2}) }), clientId: z.string().min(1, { message: t('idpClientIdRequired') }), clientSecret: z.string().min(1, { message: t('idpClientSecretRequired') }), authUrl: z.string().url({ message: t('idpErrorAuthUrlInvalid') }), tokenUrl: z.string().url({ message: t('idpErrorTokenUrlInvalid') }), identifierPath: z .string() .min(1, { message: t('idpPathRequired') }), emailPath: z.string().optional(), namePath: z.string().optional(), scopes: z.string().min(1, { message: t('idpScopeRequired') }), autoProvision: z.boolean().default(false) }); type GeneralFormValues = z.infer; const form = useForm({ resolver: zodResolver(GeneralFormSchema), defaultValues: { name: "", clientId: "", clientSecret: "", authUrl: "", tokenUrl: "", identifierPath: "sub", emailPath: "email", namePath: "name", scopes: "openid profile email", autoProvision: true } }); useEffect(() => { const loadIdp = async () => { try { const res = await api.get(`/idp/${idpId}`); if (res.status === 200) { const data = res.data.data; form.reset({ name: data.idp.name, clientId: data.idpOidcConfig.clientId, clientSecret: data.idpOidcConfig.clientSecret, authUrl: data.idpOidcConfig.authUrl, tokenUrl: data.idpOidcConfig.tokenUrl, identifierPath: data.idpOidcConfig.identifierPath, emailPath: data.idpOidcConfig.emailPath, namePath: data.idpOidcConfig.namePath, scopes: data.idpOidcConfig.scopes, autoProvision: data.idp.autoProvision }); } } catch (e) { toast({ title: t('error'), description: formatAxiosError(e), variant: "destructive" }); router.push("/admin/idp"); } finally { setInitialLoading(false); } }; loadIdp(); }, [idpId, api, form, router]); async function onSubmit(data: GeneralFormValues) { setLoading(true); try { const payload = { name: data.name, clientId: data.clientId, clientSecret: data.clientSecret, authUrl: data.authUrl, tokenUrl: data.tokenUrl, identifierPath: data.identifierPath, emailPath: data.emailPath, namePath: data.namePath, autoProvision: data.autoProvision, scopes: data.scopes }; const res = await api.post(`/idp/${idpId}/oidc`, payload); if (res.status === 200) { toast({ title: t('success'), description: t('idpUpdatedDescription') }); router.refresh(); } } catch (e) { toast({ title: t('error'), description: formatAxiosError(e), variant: "destructive" }); } finally { setLoading(false); } } if (initialLoading) { return null; } return ( <> {t('idpTitle')} {t('idpSettingsDescription')} {t('redirectUrl')} {t('redirectUrlAbout')} {t('redirectUrlAboutDescription')}
( {t('name')} {t('idpDisplayName')} )} />
{ form.setValue( "autoProvision", checked ); }} />
{t('idpAutoProvisionUsersDescription')}
{t('idpOidcConfigure')} {t('idpOidcConfigureDescription')}
( {t('idpClientId')} {t('idpClientIdDescription')} )} /> ( {t('idpClientSecret')} {t('idpClientSecretDescription')} )} /> ( {t('idpAuthUrl')} {t('idpAuthUrlDescription')} )} /> ( {t('idpTokenUrl')} {t('idpTokenUrlDescription')} )} />
{t('idpToken')} {t('idpTokenDescription')}
{t('idpJmespathAbout')} {t('idpJmespathAboutDescription')} {t('idpJmespathAboutDescriptionLink')}{" "} ( {t('idpJmespathLabel')} {t('idpJmespathLabelDescription')} )} /> ( {t('idpJmespathEmailPathOptional')} {t('idpJmespathEmailPathOptionalDescription')} )} /> ( {t('idpJmespathNamePathOptional')} {t('idpJmespathNamePathOptionalDescription')} )} /> ( {t('idpOidcConfigureScopes')} {t('idpOidcConfigureScopesDescription')} )} />
); }