From b1af506b996fe6b59f1af62e4fb267aab9b2fcfb Mon Sep 17 00:00:00 2001 From: Till JS Date: Tue, 31 Mar 2026 17:52:47 +0200 Subject: [PATCH] fix(auth): surface email-not-verified error and detect needsVerification on signup - mana-auth login route: catch Better Auth's email verification error and return 403 EMAIL_NOT_VERIFIED instead of 401 Invalid credentials - shared-auth signUp: detect emailVerified:false in register response and return needsVerification:true so the UI shows the verification prompt - shared-auth-ui LoginPage: map INVALID_CREDENTIALS error code to friendly message - shared-i18n: add invalidCredentials translation (de/en) Co-Authored-By: Claude Sonnet 4.6 --- packages/shared-auth/src/core/authService.ts | 9 ++++----- packages/shared-i18n/src/translations/auth/de.json | 1 + packages/shared-i18n/src/translations/auth/en.json | 1 + packages/shared-i18n/src/translations/auth/index.ts | 1 + services/mana-auth/src/routes/auth.ts | 8 ++++++++ 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/shared-auth/src/core/authService.ts b/packages/shared-auth/src/core/authService.ts index 761e0a722..44ec31cd2 100644 --- a/packages/shared-auth/src/core/authService.ts +++ b/packages/shared-auth/src/core/authService.ts @@ -169,13 +169,12 @@ export function createAuthService(config: AuthServiceConfig): AuthServiceInterfa return { success: false, error: errorData.message || 'Sign up failed' }; } - // Consume response to avoid unhandled promise - await response.json(); + const data = await response.json(); - // Mana Core Auth returns user data immediately on registration - // User needs to sign in separately to get tokens + // If emailVerified is false, the user needs to verify their email before login + const needsVerification = data?.user?.emailVerified === false; trackAuth('signup', { method: 'email' }); - return { success: true, needsVerification: false }; + return { success: true, needsVerification }; } catch (error) { console.error('Error signing up:', error); trackAuth('signup_failed', { method: 'email' }); diff --git a/packages/shared-i18n/src/translations/auth/de.json b/packages/shared-i18n/src/translations/auth/de.json index 1202877a0..0b3bc330f 100644 --- a/packages/shared-i18n/src/translations/auth/de.json +++ b/packages/shared-i18n/src/translations/auth/de.json @@ -19,6 +19,7 @@ "emailInvalid": "Bitte gib eine gültige E-Mail-Adresse ein", "passwordRequired": "Passwort ist erforderlich", "signInFailed": "Anmeldung fehlgeschlagen", + "invalidCredentials": "Ungültige E-Mail oder Passwort", "signInSuccess": "Erfolgreich angemeldet. Weiterleitung...", "emailVerified": "E-Mail erfolgreich bestätigt! Bitte melde dich an.", "emailNotVerified": "E-Mail nicht bestätigt.", diff --git a/packages/shared-i18n/src/translations/auth/en.json b/packages/shared-i18n/src/translations/auth/en.json index 55e6e4dee..fd73cbcf5 100644 --- a/packages/shared-i18n/src/translations/auth/en.json +++ b/packages/shared-i18n/src/translations/auth/en.json @@ -19,6 +19,7 @@ "emailInvalid": "Please enter a valid email address", "passwordRequired": "Password is required", "signInFailed": "Sign in failed", + "invalidCredentials": "Invalid email or password", "signInSuccess": "Successfully signed in. Redirecting...", "emailVerified": "Email successfully verified! Please sign in.", "emailNotVerified": "Email not verified.", diff --git a/packages/shared-i18n/src/translations/auth/index.ts b/packages/shared-i18n/src/translations/auth/index.ts index fd69bfab4..b88c64001 100644 --- a/packages/shared-i18n/src/translations/auth/index.ts +++ b/packages/shared-i18n/src/translations/auth/index.ts @@ -34,6 +34,7 @@ export interface AuthTranslations { emailInvalid: string; passwordRequired: string; signInFailed: string; + invalidCredentials?: string; signInSuccess: string; emailVerified?: string; emailNotVerified?: string; diff --git a/services/mana-auth/src/routes/auth.ts b/services/mana-auth/src/routes/auth.ts index 4e600798b..a052f735d 100644 --- a/services/mana-auth/src/routes/auth.ts +++ b/services/mana-auth/src/routes/auth.ts @@ -131,6 +131,14 @@ export function createAuthRoutes( return c.json(response); } catch (error) { + // Check if Better Auth rejected login due to unverified email + const errorMessage = error instanceof Error ? error.message : String(error); + const isEmailNotVerified = + errorMessage.includes('email') && errorMessage.toLowerCase().includes('verif'); + if (isEmailNotVerified) { + return c.json({ error: 'Email not verified', code: 'EMAIL_NOT_VERIFIED' }, 403); + } + security.logEvent({ eventType: 'LOGIN_FAILURE', ipAddress: ip,