diff --git a/apps/dashboard/src/components/user-dialog.tsx b/apps/dashboard/src/components/user-dialog.tsx index 4e6daf8ef..9f4d9b91f 100644 --- a/apps/dashboard/src/components/user-dialog.tsx +++ b/apps/dashboard/src/components/user-dialog.tsx @@ -2,7 +2,7 @@ import { useAdminApp } from "@/app/(main)/(protected)/projects/[projectId]/use-a import { ServerUser } from "@stackframe/stack"; import { KnownErrors } from "@stackframe/stack-shared"; import { emailSchema, jsonStringOrEmptySchema, passwordSchema } from "@stackframe/stack-shared/dist/schema-fields"; -import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Typography, useToast } from "@stackframe/stack-ui"; +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Button, Typography, useToast } from "@stackframe/stack-ui"; import * as yup from "yup"; import { FormDialog } from "./form-dialog"; import { DateField, InputField, SwitchField, TextAreaField } from "./form-fields"; @@ -33,6 +33,7 @@ export function UserDialog(props: { serverMetadata: props.user.serverMetadata == null ? "" : JSON.stringify(props.user.serverMetadata, null, 2), passwordEnabled: props.user.hasPassword, otpAuthEnabled: props.user.otpAuthEnabled, + updatePassword: false, }; } else { defaultValues = { @@ -48,7 +49,16 @@ export function UserDialog(props: { clientReadOnlyMetadata: jsonStringOrEmptySchema.default("null"), serverMetadata: jsonStringOrEmptySchema.default("null"), primaryEmailVerified: yup.boolean().optional(), - password: passwordSchema.optional(), + password: passwordSchema.test({ + name: 'password-required', + message: "Password is required", + test: (value, context) => { + if (context.parent.passwordEnabled && (context.parent.updatePassword || props.type === 'create')) { + return value != null; + } + return true; + }, + }).optional(), otpAuthEnabled: yup.boolean().test({ name: 'otp-verified', message: "Primary email must be verified if OTP/magic link sign-in is enabled", @@ -57,6 +67,7 @@ export function UserDialog(props: { }, }).optional(), passwordEnabled: yup.boolean().optional(), + updatePassword: yup.boolean().optional(), }); async function handleSubmit(values: yup.InferType) { @@ -113,7 +124,25 @@ export function UserDialog(props: { {project.config.magicLinkEnabled && } {project.config.credentialEnabled && } - {form.watch("passwordEnabled") ? : null} + {form.watch("passwordEnabled") && ( + props.type === 'edit' && !form.watch("password") && !form.watch("updatePassword") ? ( + + ) : ( + + ) + )} {!form.watch("primaryEmailVerified") && form.watch("otpAuthEnabled") && Primary email must be verified if OTP/magic link sign-in is enabled} diff --git a/packages/stack-shared/src/schema-fields.ts b/packages/stack-shared/src/schema-fields.ts index 2838c557f..c3bce768f 100644 --- a/packages/stack-shared/src/schema-fields.ts +++ b/packages/stack-shared/src/schema-fields.ts @@ -212,7 +212,7 @@ export const base64Schema = yupString().test("is-base64", (params) => `${params. if (value == null) return true; return isBase64(value); }); -export const passwordSchema = yupString().max(70); +export const passwordSchema = yupString().min(1).max(70); /** * A stricter email schema that does some additional checks for UX input. (Some emails are allowed by the spec, for