Files
docmost/apps/client/src/ee/security/components/sso-oidc-form.tsx
Philip Okugbe b81c9ee10c feat: cloud and ee (#805)
* stripe init
git submodules for enterprise modules

* * Cloud billing UI - WIP
* Proxy websockets in dev mode
* Separate workspace login and creation for cloud
* Other fixes

* feat: billing (cloud)

* * add domain service
* prepare links from workspace hostname

* WIP

* Add exchange token generation
* Validate JWT token type during verification

* domain service

* add SkipTransform decorator

* * updates (server)
* add new packages
* new sso migration file

* WIP

* Fix hostname generation

* WIP

* WIP

* Reduce input error font-size
* set max password length

* jwt package

* license page - WIP

* * License management UI
* Move license key store to db

* add reflector

* SSO enforcement

* * Add default plan
* Add usePlan hook

* * Fix auth container margin in mobile
* Redirect login and home to select page in cloud

* update .gitignore

* Default to yearly

* * Trial messaging
* Handle ended trials

* Don't set to readonly on collab disconnect (Cloud)

* Refine trial (UI)
* Fix bug caused by using jotai optics atom in AppHeader component

* configurable database maximum pool

* Close SSO form on save

* wip

* sync

* Only show sign-in in cloud

* exclude base api part from workspaceId check

* close db connection beforeApplicationShutdown

* Add health/live endpoint

* clear cookie on hostname change

* reset currentUser atom

* Change text

* return 401 if workspace does not match

* feat: show user workspace list in cloud login page

* sync

* Add home path

* Prefetch to speed up queries

* * Add robots.txt
* Disallow login and forgot password routes

* wildcard user-agent

* Fix space query cache

* fix

* fix

* use space uuid for recent pages

* prefetch billing plans

* enhance license page

* sync
2025-03-06 13:38:37 +00:00

141 lines
4.4 KiB
TypeScript

import React from "react";
import { z } from "zod";
import { useForm, zodResolver } from "@mantine/form";
import { Box, Button, Group, Stack, Switch, TextInput } from "@mantine/core";
import { buildCallbackUrl } from "@/ee/security/sso.utils.ts";
import classes from "@/ee/security/components/sso.module.css";
import { IAuthProvider } from "@/ee/security/types/security.types.ts";
import CopyTextButton from "@/components/common/copy.tsx";
import { useTranslation } from "react-i18next";
import { useUpdateSsoProviderMutation } from "@/ee/security/queries/security-query.ts";
const ssoSchema = z.object({
name: z.string().min(1, "Display name is required"),
oidcIssuer: z.string().url(),
oidcClientId: z.string().min(1, "Client id is required"),
oidcClientSecret: z.string().min(1, "Client secret is required"),
isEnabled: z.boolean(),
allowSignup: z.boolean(),
});
type SSOFormValues = z.infer<typeof ssoSchema>;
interface SsoFormProps {
provider: IAuthProvider;
onClose?: () => void;
}
export function SsoOIDCForm({ provider, onClose }: SsoFormProps) {
const { t } = useTranslation();
const updateSsoProviderMutation = useUpdateSsoProviderMutation();
const form = useForm<SSOFormValues>({
initialValues: {
name: provider.name || "",
oidcIssuer: provider.oidcIssuer || "",
oidcClientId: provider.oidcClientId || "",
oidcClientSecret: provider.oidcClientSecret || "",
isEnabled: provider.isEnabled,
allowSignup: provider.allowSignup,
},
validate: zodResolver(ssoSchema),
});
const callbackUrl = buildCallbackUrl({
providerId: provider.id,
type: provider.type,
});
const handleSubmit = async (values: SSOFormValues) => {
const ssoData: Partial<IAuthProvider> = {
providerId: provider.id,
};
if (form.isDirty("name")) {
ssoData.name = values.name;
}
if (form.isDirty("oidcIssuer")) {
ssoData.oidcIssuer = values.oidcIssuer;
}
if (form.isDirty("oidcClientId")) {
ssoData.oidcClientId = values.oidcClientId;
}
if (form.isDirty("oidcClientSecret")) {
ssoData.oidcClientSecret = values.oidcClientSecret;
}
if (form.isDirty("isEnabled")) {
ssoData.isEnabled = values.isEnabled;
}
if (form.isDirty("allowSignup")) {
ssoData.allowSignup = values.allowSignup;
}
await updateSsoProviderMutation.mutateAsync(ssoData);
form.resetDirty();
onClose();
};
return (
<Box maw={600} mx="auto">
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack>
<TextInput
label="Display name"
placeholder="e.g Google SSO"
data-autofocus
{...form.getInputProps("name")}
/>
<TextInput
label="Callback URL"
variant="filled"
value={callbackUrl}
pointer
readOnly
rightSection={<CopyTextButton text={callbackUrl} />}
/>
<TextInput
label="Issuer URL"
description="Enter your OIDC issuer URL"
placeholder="e.g https://accounts.google.com/"
{...form.getInputProps("oidcIssuer")}
/>
<TextInput
label="Client ID"
description="Enter your OIDC ClientId"
placeholder="e.g 292085223830.apps.googleusercontent.com"
{...form.getInputProps("oidcClientId")}
/>
<TextInput
label="Client Secret"
description="Enter your OIDC Client Secret"
placeholder="e.g OCSPX-zVCkotEPGRnJA1XKUrbgjlf7PQQ-"
{...form.getInputProps("oidcClientSecret")}
/>
<Group justify="space-between">
<div>{t("Allow signup")}</div>
<Switch
className={classes.switch}
checked={form.values.allowSignup}
{...form.getInputProps("allowSignup")}
/>
</Group>
<Group justify="space-between">
<div>{t("Enabled")}</div>
<Switch
className={classes.switch}
checked={form.values.isEnabled}
{...form.getInputProps("isEnabled")}
/>
</Group>
<Group mt="md" justify="flex-end">
<Button type="submit" disabled={!form.isDirty()}>
{t("Save")}
</Button>
</Group>
</Stack>
</form>
</Box>
);
}