| 117 | |
| 118 | #### Classes #################################################### |
| 119 | async def authenticate_user( |
| 120 | request: Request, |
| 121 | email: str, |
| 122 | password: str, |
| 123 | db_session: AsyncSession, |
| 124 | ) -> User | bool: |
| 125 | user = await security_get_user(request, db_session, email) |
| 126 | if not user: |
| 127 | # SECURITY: run a real password-verify against a dummy hash so |
| 128 | # unknown-user responses take roughly the same time as known-user |
| 129 | # wrong-password responses. Without this, an attacker can enumerate |
| 130 | # accounts via response timing alone (Argon2 verify is slow, skipping |
| 131 | # it is fast). |
| 132 | security_verify_password(password, _DUMMY_PASSWORD_HASH) |
| 133 | return False |
| 134 | if not security_verify_password(password, user.password): |
| 135 | return False |
| 136 | return user |
| 137 | |
| 138 | |
| 139 | def create_access_token(data: dict, expires_delta: timedelta | None = None): |