From 259253e7b36dda54244ab5127fb8c4943de44d8c Mon Sep 17 00:00:00 2001 From: Till JS Date: Tue, 31 Mar 2026 18:44:01 +0200 Subject: [PATCH] feat(auth): show resend verification panel when registering with existing unverified email - auth.ts: catch USER_ALREADY_EXISTS and return EMAIL_ALREADY_REGISTERED (409) - authService: map 409 with EMAIL_ALREADY_REGISTERED code to typed error - RegisterPage: show amber warning panel + resend + go-to-login for existing emails - translations: add emailAlreadyRegistered, emailAlreadyRegisteredMessage, goToLogin (en/de) Co-Authored-By: Claude Sonnet 4.6 --- .../src/pages/RegisterPage.svelte | 124 +++++++++++++----- packages/shared-auth/src/core/authService.ts | 8 +- .../shared-i18n/src/translations/auth/de.json | 5 +- .../shared-i18n/src/translations/auth/en.json | 5 +- .../src/translations/auth/index.ts | 3 + services/mana-auth/src/routes/auth.ts | 27 ++-- 6 files changed, 129 insertions(+), 43 deletions(-) diff --git a/packages/shared-auth-ui/src/pages/RegisterPage.svelte b/packages/shared-auth-ui/src/pages/RegisterPage.svelte index 8213e0772..67d502118 100644 --- a/packages/shared-auth-ui/src/pages/RegisterPage.svelte +++ b/packages/shared-auth-ui/src/pages/RegisterPage.svelte @@ -33,6 +33,9 @@ resendingVerification?: string; verificationEmailSent?: string; checkYourEmail?: string; + emailAlreadyRegistered?: string; + emailAlreadyRegisteredMessage?: string; + goToLogin?: string; } /** Default English translations */ @@ -63,6 +66,10 @@ resendingVerification: 'Sending...', verificationEmailSent: 'Verification email sent! Please check your inbox.', checkYourEmail: 'Check your email', + emailAlreadyRegistered: 'Email already registered', + emailAlreadyRegisteredMessage: + "An account with this email already exists. If you haven't verified your email yet, resend the verification email.", + goToLogin: 'Sign in instead', }; interface Props { @@ -124,6 +131,7 @@ let showConfirmPassword = $state(false); let resendingVerification = $state(false); let verificationEmailSent = $state(false); + let emailAlreadyRegistered = $state(false); // Theme state - can be toggled manually, defaults to system preference let userThemePreference = $state<'light' | 'dark' | null>(null); @@ -217,6 +225,12 @@ } else { goto(successRedirect); } + } else if (result.error === 'EMAIL_ALREADY_REGISTERED') { + emailAlreadyRegistered = true; + needsVerification = true; + success = true; + password = ''; + confirmPassword = ''; } else { error = result.error || t.registrationFailed; } @@ -322,54 +336,88 @@ {/if} - + {#if success && needsVerification} -
+
- - - + {#if emailAlreadyRegistered} + + + + {:else} + + + + {/if}

- {t.checkYourEmail || 'Check your email'} + {emailAlreadyRegistered + ? t.emailAlreadyRegistered || 'Email already registered' + : t.checkYourEmail || 'Check your email'}

- {t.accountCreated} + {emailAlreadyRegistered + ? t.emailAlreadyRegisteredMessage || + "An account with this email already exists. If you haven't verified your email yet, resend the verification email." + : t.accountCreated}

- {#if onResendVerification} -
-

- Didn't receive the email? -

+
+ {#if onResendVerification}
- {/if} + {/if} + {#if emailAlreadyRegistered} + + {/if} +
{/if} diff --git a/packages/shared-auth/src/core/authService.ts b/packages/shared-auth/src/core/authService.ts index 44ec31cd2..4f4c21a52 100644 --- a/packages/shared-auth/src/core/authService.ts +++ b/packages/shared-auth/src/core/authService.ts @@ -161,7 +161,13 @@ export function createAuthService(config: AuthServiceConfig): AuthServiceInterfa const errorData = await response.json(); if (response.status === 409) { - return { success: false, error: 'Email already in use' }; + return { + success: false, + error: + errorData.code === 'EMAIL_ALREADY_REGISTERED' + ? 'EMAIL_ALREADY_REGISTERED' + : 'Email already in use', + }; } else if (response.status === 400) { return { success: false, error: errorData.message || 'Invalid email or password' }; } diff --git a/packages/shared-i18n/src/translations/auth/de.json b/packages/shared-i18n/src/translations/auth/de.json index 0b3bc330f..bde838b37 100644 --- a/packages/shared-i18n/src/translations/auth/de.json +++ b/packages/shared-i18n/src/translations/auth/de.json @@ -49,7 +49,10 @@ "resendVerification": "Bestätigungs-E-Mail erneut senden", "resendingVerification": "Wird gesendet...", "verificationEmailSent": "Bestätigungs-E-Mail wurde gesendet! Bitte überprüfe deinen Posteingang.", - "checkYourEmail": "Überprüfe deine E-Mails" + "checkYourEmail": "Überprüfe deine E-Mails", + "emailAlreadyRegistered": "E-Mail bereits registriert", + "emailAlreadyRegisteredMessage": "Ein Konto mit dieser E-Mail-Adresse existiert bereits. Falls du deine E-Mail noch nicht bestätigt hast, sende die Bestätigungs-E-Mail erneut.", + "goToLogin": "Stattdessen anmelden" }, "forgotPassword": { "titleForm": "Passwort zurücksetzen", diff --git a/packages/shared-i18n/src/translations/auth/en.json b/packages/shared-i18n/src/translations/auth/en.json index fd73cbcf5..85a8b2f1a 100644 --- a/packages/shared-i18n/src/translations/auth/en.json +++ b/packages/shared-i18n/src/translations/auth/en.json @@ -49,7 +49,10 @@ "resendVerification": "Resend verification email", "resendingVerification": "Sending...", "verificationEmailSent": "Verification email sent! Please check your inbox.", - "checkYourEmail": "Check your email" + "checkYourEmail": "Check your email", + "emailAlreadyRegistered": "Email already registered", + "emailAlreadyRegisteredMessage": "An account with this email already exists. If you haven't verified your email yet, resend the verification email.", + "goToLogin": "Sign in instead" }, "forgotPassword": { "titleForm": "Reset Password", diff --git a/packages/shared-i18n/src/translations/auth/index.ts b/packages/shared-i18n/src/translations/auth/index.ts index b88c64001..6faa66286 100644 --- a/packages/shared-i18n/src/translations/auth/index.ts +++ b/packages/shared-i18n/src/translations/auth/index.ts @@ -65,6 +65,9 @@ export interface AuthTranslations { resendingVerification?: string; verificationEmailSent?: string; checkYourEmail?: string; + emailAlreadyRegistered?: string; + emailAlreadyRegisteredMessage?: string; + goToLogin?: string; }; forgotPassword: { titleForm: string; diff --git a/services/mana-auth/src/routes/auth.ts b/services/mana-auth/src/routes/auth.ts index 561b0b6fe..0689282f5 100644 --- a/services/mana-auth/src/routes/auth.ts +++ b/services/mana-auth/src/routes/auth.ts @@ -53,14 +53,25 @@ export function createAuthRoutes( sourceAppStore.set(body.email, body.sourceAppUrl); } - const response = await auth.api.signUpEmail({ - body: { - email: body.email, - password: body.password, - name: body.name || body.email.split('@')[0], - }, - headers: c.req.raw.headers, - }); + let response; + try { + response = await auth.api.signUpEmail({ + body: { + email: body.email, + password: body.password, + name: body.name || body.email.split('@')[0], + }, + headers: c.req.raw.headers, + }); + } catch (error) { + const isUserExists = + (error as any)?.body?.code === 'USER_ALREADY_EXISTS' || + (error as any)?.status === 'UNPROCESSABLE_ENTITY'; + if (isUserExists) { + return c.json({ error: 'Email already registered', code: 'EMAIL_ALREADY_REGISTERED' }, 409); + } + throw error; + } if (response?.user?.id) { security.logEvent({ userId: response.user.id, eventType: 'REGISTER' });