diff --git a/surfsense_backend/app/users.py b/surfsense_backend/app/users.py index ee07ba88f..81d073559 100644 --- a/surfsense_backend/app/users.py +++ b/surfsense_backend/app/users.py @@ -2,7 +2,7 @@ import logging import uuid import httpx -from fastapi import Depends, Request, Response +from fastapi import Depends, HTTPException, Request, Response from fastapi.responses import JSONResponse, RedirectResponse from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin, models from fastapi_users.authentication import ( @@ -180,6 +180,32 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]): ): print(f"Verification requested for user {user.id}. Verification token: {token}") + async def authenticate(self, credentials): + """ + Override to return a specific error when user account doesn't exist, + instead of a generic LOGIN_BAD_CREDENTIALS. + """ + from fastapi_users.exceptions import UserNotExists + + try: + user = await self.get_by_email(credentials.username) + except UserNotExists: + # Still hash the password to mitigate timing attacks + self.password_helper.hash(credentials.password) + raise HTTPException( + status_code=400, + detail="LOGIN_USER_NOT_FOUND", + ) from None + + verified, updated_password_hash = self.password_helper.verify_and_update( + credentials.password, user.hashed_password + ) + if not verified: + return None + if updated_password_hash is not None: + await self.user_db.update(user, {"hashed_password": updated_password_hash}) + return user + async def get_user_manager(user_db: SQLAlchemyUserDatabase = Depends(get_user_db)): yield UserManager(user_db) diff --git a/surfsense_web/lib/auth-errors.ts b/surfsense_web/lib/auth-errors.ts index ce50c9c99..c761e41f8 100644 --- a/surfsense_web/lib/auth-errors.ts +++ b/surfsense_web/lib/auth-errors.ts @@ -45,6 +45,10 @@ const AUTH_ERROR_MESSAGES: AuthErrorMapping = { title: "Invalid credentials", description: "The email or password you entered is incorrect", }, + LOGIN_USER_NOT_FOUND: { + title: "Account not found", + description: "No account exists with this email address. Please sign up first.", + }, LOGIN_USER_NOT_VERIFIED: { title: "Account not verified", description: "Please verify your email address before signing in", @@ -139,6 +143,7 @@ export function getAuthErrorMessage(errorCode: string, returnTitle: boolean = fa if (!errorInfo) { const patterns = [ { pattern: /credential|password|email/i, code: "LOGIN_BAD_CREDENTIALS" }, + { pattern: /not found|no account|does not exist|user not found/i, code: "LOGIN_USER_NOT_FOUND" }, { pattern: /verify|verification/i, code: "LOGIN_USER_NOT_VERIFIED" }, { pattern: /inactive|disabled|suspended/i, code: "USER_INACTIVE" }, { pattern: /exists|duplicate/i, code: "REGISTER_USER_ALREADY_EXISTS" },