feat: enhance user authentication by adding specific error handling for non-existent accounts

This commit is contained in:
Anish Sarkar 2026-02-08 17:10:40 +05:30
parent 20a13df7e7
commit 54b4501ca6
2 changed files with 32 additions and 1 deletions

View file

@ -2,7 +2,7 @@ import logging
import uuid import uuid
import httpx import httpx
from fastapi import Depends, Request, Response from fastapi import Depends, HTTPException, Request, Response
from fastapi.responses import JSONResponse, RedirectResponse from fastapi.responses import JSONResponse, RedirectResponse
from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin, models from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin, models
from fastapi_users.authentication import ( 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}") 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)): async def get_user_manager(user_db: SQLAlchemyUserDatabase = Depends(get_user_db)):
yield UserManager(user_db) yield UserManager(user_db)

View file

@ -45,6 +45,10 @@ const AUTH_ERROR_MESSAGES: AuthErrorMapping = {
title: "Invalid credentials", title: "Invalid credentials",
description: "The email or password you entered is incorrect", 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: { LOGIN_USER_NOT_VERIFIED: {
title: "Account not verified", title: "Account not verified",
description: "Please verify your email address before signing in", description: "Please verify your email address before signing in",
@ -139,6 +143,7 @@ export function getAuthErrorMessage(errorCode: string, returnTitle: boolean = fa
if (!errorInfo) { if (!errorInfo) {
const patterns = [ const patterns = [
{ pattern: /credential|password|email/i, code: "LOGIN_BAD_CREDENTIALS" }, { 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: /verify|verification/i, code: "LOGIN_USER_NOT_VERIFIED" },
{ pattern: /inactive|disabled|suspended/i, code: "USER_INACTIVE" }, { pattern: /inactive|disabled|suspended/i, code: "USER_INACTIVE" },
{ pattern: /exists|duplicate/i, code: "REGISTER_USER_ALREADY_EXISTS" }, { pattern: /exists|duplicate/i, code: "REGISTER_USER_ALREADY_EXISTS" },