fix: invitation accept sent empty password string for existing users
AcceptInvitationPage's form always initializes password to "" via
react-hook-form defaultValues, even when the invited email already has
an account and the password field is hidden (requiresPassword=false).
The empty string was sent as-is instead of omitted, tripping the
backend's password.min(8).optional() — which only treats an absent key
as optional, not an empty string — and surfacing Zod's raw untranslated
message ("Too small: expected string to have >=8 characters") instead
of a friendly one.
Frontend: omit the password key entirely from the payload when
requiresPassword is false. Backend: defense in depth — treat an empty
string the same as omitted via a transform, and give the min(8) check
an actual German message for if this path is ever hit another way.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
This commit is contained in:
1 parent
ad6efc4afa
commit
f9451881c4
2 files changed
+20
-4
No files matched your search
@@ -32,9 +32,17 @@ const createInvitationSchema = z.object({
|
||||
});
|
||||
|
||||
const acceptInvitationSchema = z.object({
|
||||
firstName: z.string().min(1),
|
||||
lastName: z.string().min(1),
|
||||
password: z.string().min(8).optional(),
|
||||
firstName: z.string().min(1, "Pflichtfeld"),
|
||||
lastName: z.string().min(1, "Pflichtfeld"),
|
||||
// Accepts empty string as "not provided" in addition to omitted, so a
|
||||
// client that sends "" instead of omitting the key (e.g. an unchanged
|
||||
// React Hook Form defaultValue for an already-registered email) doesn't
|
||||
// surface a raw Zod message — falls back to the friendly min(8) message.
|
||||
password: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((v) => (v === "" ? undefined : v))
|
||||
.pipe(z.string().min(8, "Passwort muss mindestens 8 Zeichen haben").optional()),
|
||||
});
|
||||
|
||||
async function provisionMatrixUser(
|
||||
|
||||
@@ -66,10 +66,18 @@ export function AcceptInvitationPage() {
|
||||
async function onSubmit(values: z.infer<typeof schema>) {
|
||||
setSubmitError(null);
|
||||
try {
|
||||
// When the invited email already has a login (requiresPassword=false),
|
||||
// the hidden password field still holds its defaultValue of "" — sending
|
||||
// that empty string (instead of omitting the key) hit the backend's
|
||||
// min(8) check and surfaced its raw Zod message instead of a friendly one.
|
||||
const payload = preview?.requiresPassword
|
||||
? values
|
||||
: { firstName: values.firstName, lastName: values.lastName };
|
||||
|
||||
const res = await fetch(`/api/invitations/${token}/accept`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(values),
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const body = (await res.json()) as { error?: string };
|
||||
|
||||
Reference in new issue
Block a user