diff --git a/docs/usage/routes.md b/docs/usage/routes.md index 3512f0d6..9d1915e7 100644 --- a/docs/usage/routes.md +++ b/docs/usage/routes.md @@ -2,7 +2,9 @@ You'll find here the routes exposed by **FastAPI Users**. Note that you can also review them through the [interactive API docs](https://fastapi.tiangolo.com/tutorial/first-steps/#interactive-api-docs). -## `POST /register` +## Unauthenticated + +### `POST /register` Register a new user. @@ -29,7 +31,7 @@ Register a new user. !!! fail "`400 Bad Request`" A user already exists with this email. -## `POST /login` +### `POST /login` Login a user. @@ -50,7 +52,7 @@ Login a user. !!! fail "`400 Bad Request`" Bad credentials or the user is inactive. -## `POST /forgot-password` +### `POST /forgot-password` Request a reset password procedure. Will generate a temporary token and call the defined `on_after_forgot_password` if the user exists. @@ -65,7 +67,7 @@ To prevent malicious users from guessing existing users in your databse, the rou !!! success "`202 Accepted`" -## `POST /reset-password` +### `POST /reset-password` Reset a password. Requires the token generated by the `/forgot-password` route. @@ -77,9 +79,28 @@ Reset a password. Requires the token generated by the `/forgot-password` route. } ``` -!!! success "`200 Accepted`" +!!! success "`200 OK`" !!! fail "`422 Validation Error`" !!! fail "`400 Bad Request`" Bad or expired token. + +## Authenticated + +### `GET /me` + +Return the current authenticated active user. + +!!! success "`200 OK`" + ```json + { + "id": "57cbb51a-ab71-4009-8802-3f54b4f2e23", + "email": "king.arthur@camelot.bt", + "is_active": true, + "is_superuser": false + } + ``` + +!!! fail "`401 Unauthorized`" + Missing token or inactive user. diff --git a/fastapi_users/router.py b/fastapi_users/router.py index b4bb10a0..d6962a7b 100644 --- a/fastapi_users/router.py +++ b/fastapi_users/router.py @@ -32,6 +32,8 @@ def get_user_router( on_after_forgot_password ) + get_current_active_user = auth.get_current_active_user(user_db) + @router.post( "/register", response_model=models.User, status_code=status.HTTP_201_CREATED ) @@ -102,4 +104,10 @@ def get_user_router( except jwt.PyJWTError: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST) + @router.get("/me", response_model=models.User) + async def me( + user: models.UserDB = Depends(get_current_active_user) # type: ignore + ): + return user + return router diff --git a/tests/test_router.py b/tests/test_router.py index 9836230f..97151b02 100644 --- a/tests/test_router.py +++ b/tests/test_router.py @@ -265,3 +265,27 @@ class TestResetPassword: updated_user = mock_user_db.update.call_args[0][0] assert updated_user.hashed_password != current_hashed_passord + + +class TestMe: + def test_missing_token(self, test_app_client: TestClient): + response = test_app_client.get("/me") + assert response.status_code == status.HTTP_401_UNAUTHORIZED + + def test_inactive_user( + self, test_app_client: TestClient, inactive_user: BaseUserDB + ): + response = test_app_client.get( + "/me", headers={"Authorization": f"Bearer {inactive_user.id}"} + ) + assert response.status_code == status.HTTP_401_UNAUTHORIZED + + def test_active_user(self, test_app_client: TestClient, user: BaseUserDB): + response = test_app_client.get( + "/me", headers={"Authorization": f"Bearer {user.id}"} + ) + assert response.status_code == status.HTTP_200_OK + + response_json = response.json() + assert response_json["id"] == user.id + assert response_json["email"] == user.email