mirror of
https://github.com/fastapi-users/fastapi-users.git
synced 2025-08-15 19:30:47 +08:00
@ -31,6 +31,12 @@ Register a new user. Will call the `on_after_register` [event handlers](../confi
|
||||
!!! fail "`400 Bad Request`"
|
||||
A user already exists with this email.
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "REGISTER_USER_ALREADY_EXISTS"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /login`
|
||||
|
||||
Login a user.
|
||||
@ -52,6 +58,12 @@ Login a user.
|
||||
!!! fail "`400 Bad Request`"
|
||||
Bad credentials or the user is inactive.
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "LOGIN_BAD_CREDENTIALS"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /forgot-password`
|
||||
|
||||
Request a reset password procedure. Will generate a temporary token and call the `on_after_forgot_password` [event handlers](../configuration/router.md#event-handlers) if the user exists.
|
||||
@ -86,6 +98,12 @@ Reset a password. Requires the token generated by the `/forgot-password` route.
|
||||
!!! fail "`400 Bad Request`"
|
||||
Bad or expired token.
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "RESET_PASSWORD_BAD_TOKEN"
|
||||
}
|
||||
```
|
||||
|
||||
## Authenticated
|
||||
|
||||
### `GET /me`
|
||||
|
@ -1,7 +1,7 @@
|
||||
import asyncio
|
||||
import typing
|
||||
from collections import defaultdict
|
||||
from enum import Enum
|
||||
from enum import Enum, auto
|
||||
|
||||
import jwt
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException
|
||||
@ -17,9 +17,15 @@ from fastapi_users.password import get_password_hash
|
||||
from fastapi_users.utils import JWT_ALGORITHM, generate_jwt
|
||||
|
||||
|
||||
class ErrorCode:
|
||||
REGISTER_USER_ALREADY_EXISTS = "REGISTER_USER_ALREADY_EXISTS"
|
||||
LOGIN_BAD_CREDENTIALS = "LOGIN_BAD_CREDENTIALS"
|
||||
RESET_PASSWORD_BAD_TOKEN = "RESET_PASSWORD_BAD_TOKEN"
|
||||
|
||||
|
||||
class Event(Enum):
|
||||
ON_AFTER_REGISTER = 1
|
||||
ON_AFTER_FORGOT_PASSWORD = 2
|
||||
ON_AFTER_REGISTER = auto()
|
||||
ON_AFTER_FORGOT_PASSWORD = auto()
|
||||
|
||||
|
||||
class UserRouter(APIRouter):
|
||||
@ -80,7 +86,10 @@ def get_user_router(
|
||||
existing_user = await user_db.get_by_email(user.email)
|
||||
|
||||
if existing_user is not None:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=ErrorCode.REGISTER_USER_ALREADY_EXISTS,
|
||||
)
|
||||
|
||||
hashed_password = get_password_hash(user.password)
|
||||
db_user = models.UserDB(
|
||||
@ -98,10 +107,11 @@ def get_user_router(
|
||||
):
|
||||
user = await user_db.authenticate(credentials)
|
||||
|
||||
if user is None:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST)
|
||||
elif not user.is_active:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST)
|
||||
if user is None or not user.is_active:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=ErrorCode.LOGIN_BAD_CREDENTIALS,
|
||||
)
|
||||
|
||||
return await auth.get_login_response(user, response)
|
||||
|
||||
@ -131,16 +141,25 @@ def get_user_router(
|
||||
)
|
||||
user_id = data.get("user_id")
|
||||
if user_id is None:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=ErrorCode.RESET_PASSWORD_BAD_TOKEN,
|
||||
)
|
||||
|
||||
user = await user_db.get(user_id)
|
||||
if user is None or not user.is_active:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=ErrorCode.RESET_PASSWORD_BAD_TOKEN,
|
||||
)
|
||||
|
||||
user.hashed_password = get_password_hash(password)
|
||||
await user_db.update(user)
|
||||
except jwt.PyJWTError:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=ErrorCode.RESET_PASSWORD_BAD_TOKEN,
|
||||
)
|
||||
|
||||
@router.get("/me", response_model=models.User)
|
||||
async def me(
|
||||
|
@ -47,7 +47,6 @@ async def test_get_login_response(jwt_authentication, user):
|
||||
class TestGetCurrentUser:
|
||||
def test_missing_token(self, test_auth_client):
|
||||
response = test_auth_client.get("/test-current-user")
|
||||
print(response.json())
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
def test_invalid_token(self, test_auth_client):
|
||||
|
@ -8,7 +8,7 @@ from starlette import status
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from fastapi_users.models import BaseUser, BaseUserDB
|
||||
from fastapi_users.router import Event, get_user_router
|
||||
from fastapi_users.router import ErrorCode, Event, get_user_router
|
||||
from fastapi_users.utils import JWT_ALGORITHM, generate_jwt
|
||||
|
||||
SECRET = "SECRET"
|
||||
@ -79,6 +79,7 @@ class TestRegister:
|
||||
json = {"email": "king.arthur@camelot.bt", "password": "guinevere"}
|
||||
response = test_app_client.post("/register", json=json)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.json()["detail"] == ErrorCode.REGISTER_USER_ALREADY_EXISTS
|
||||
assert event_handler.called is False
|
||||
|
||||
def test_valid_body(self, test_app_client: TestClient, event_handler):
|
||||
@ -141,11 +142,13 @@ class TestLogin:
|
||||
data = {"username": "lancelot@camelot.bt", "password": "guinevere"}
|
||||
response = test_app_client.post("/login", data=data)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.json()["detail"] == ErrorCode.LOGIN_BAD_CREDENTIALS
|
||||
|
||||
def test_wrong_password(self, test_app_client: TestClient):
|
||||
data = {"username": "king.arthur@camelot.bt", "password": "percival"}
|
||||
response = test_app_client.post("/login", data=data)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.json()["detail"] == ErrorCode.LOGIN_BAD_CREDENTIALS
|
||||
|
||||
def test_valid_credentials(self, test_app_client: TestClient, user: BaseUserDB):
|
||||
data = {"username": "king.arthur@camelot.bt", "password": "guinevere"}
|
||||
@ -157,6 +160,7 @@ class TestLogin:
|
||||
data = {"username": "percival@camelot.bt", "password": "angharad"}
|
||||
response = test_app_client.post("/login", data=data)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.json()["detail"] == ErrorCode.LOGIN_BAD_CREDENTIALS
|
||||
|
||||
|
||||
class TestForgotPassword:
|
||||
@ -213,8 +217,8 @@ class TestResetPassword:
|
||||
def test_invalid_token(self, test_app_client: TestClient):
|
||||
json = {"token": "foo", "password": "guinevere"}
|
||||
response = test_app_client.post("/reset-password", json=json)
|
||||
print(response.json(), response.status_code)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.json()["detail"] == ErrorCode.RESET_PASSWORD_BAD_TOKEN
|
||||
|
||||
def test_valid_token_missing_user_id_payload(
|
||||
self, mocker, mock_user_db, test_app_client: TestClient, forgot_password_token
|
||||
@ -224,6 +228,7 @@ class TestResetPassword:
|
||||
json = {"token": forgot_password_token(), "password": "holygrail"}
|
||||
response = test_app_client.post("/reset-password", json=json)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.json()["detail"] == ErrorCode.RESET_PASSWORD_BAD_TOKEN
|
||||
assert mock_user_db.update.called is False
|
||||
|
||||
def test_inactive_user(
|
||||
@ -242,6 +247,7 @@ class TestResetPassword:
|
||||
}
|
||||
response = test_app_client.post("/reset-password", json=json)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.json()["detail"] == ErrorCode.RESET_PASSWORD_BAD_TOKEN
|
||||
assert mock_user_db.update.called is False
|
||||
|
||||
def test_existing_user(
|
||||
|
Reference in New Issue
Block a user