Skip to content

Commit

Permalink
Return 403 instead of 401 when a user is known (#705)
Browse files Browse the repository at this point in the history
* return 403 instead of 401 if user is known

* return 403 for unverified users

* updated docs
  • Loading branch information
daanbeverdam committed Sep 4, 2021
1 parent 7527902 commit e59fb2c
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 33 deletions.
4 changes: 2 additions & 2 deletions docs/usage/dependency-callables.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Return a dependency callable to retrieve currently authenticated user, passing t

* `optional`: If `True`, `None` is returned if there is no authenticated user or if it doesn't pass the other requirements. Otherwise, throw `401 Unauthorized`. Defaults to `False`.
* `active`: If `True`, throw `401 Unauthorized` if the authenticated user is inactive. Defaults to `False`.
* `verified`: If `True`, throw `401 Unauthorized` if the authenticated user is not verified. Defaults to `False`.
* `verified`: If `True`, throw `403 Forbidden` if the authenticated user is not verified. Defaults to `False`.
* `superuser`: If `True`, throw `403 Forbidden` if the authenticated user is not a superuser. Defaults to `False`.

### Examples
Expand Down Expand Up @@ -88,7 +88,7 @@ def protected_route(user: User = Depends(fastapi_users.get_current_active_user))

### `get_current_verified_user`

Get the current active and verified user. Will throw a `401 Unauthorized` if missing or wrong credentials or if the user is not active and verified.
Get the current active and verified user. Will throw a `401 Unauthorized` if missing or wrong credentials or if the user is not active. Will throw a `403 Forbidden` if the user is unverified.

```py
@app.get("/protected-route")
Expand Down
3 changes: 2 additions & 1 deletion fastapi_users/authentication/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,12 +183,13 @@ async def _authenticate(

status_code = status.HTTP_401_UNAUTHORIZED
if user:
status_code = status.HTTP_403_FORBIDDEN
if active and not user.is_active:
status_code = status.HTTP_401_UNAUTHORIZED
user = None
elif verified and not user.is_verified:
user = None
elif superuser and not user.is_superuser:
status_code = status.HTTP_403_FORBIDDEN
user = None

if not user and not optional:
Expand Down
6 changes: 3 additions & 3 deletions tests/test_fastapi_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ async def test_valid_token_unverified_user(
"/current-verified-user",
headers={"Authorization": f"Bearer {user.id}"},
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN

async def test_valid_token_verified_user(
self, test_app_client: httpx.AsyncClient, verified_user: UserDB
Expand Down Expand Up @@ -253,7 +253,7 @@ async def test_valid_token_regular_user(
"/current-verified-superuser",
headers={"Authorization": f"Bearer {user.id}"},
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN

async def test_valid_token_verified_user(
self, test_app_client: httpx.AsyncClient, verified_user: UserDB
Expand All @@ -271,7 +271,7 @@ async def test_valid_token_superuser(
"/current-verified-superuser",
headers={"Authorization": f"Bearer {superuser.id}"},
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN

async def test_valid_token_verified_superuser(
self, test_app_client: httpx.AsyncClient, verified_superuser: UserDB
Expand Down
2 changes: 1 addition & 1 deletion tests/test_router_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ async def test_valid_credentials_unverified(
path, headers={"Authorization": f"Bearer {user.id}"}
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_200_OK

Expand Down
46 changes: 20 additions & 26 deletions tests/test_router_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ async def test_active_user(
"/me", headers={"Authorization": f"Bearer {user.id}"}
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_200_OK
data = cast(Dict[str, Any], response.json())
Expand Down Expand Up @@ -159,7 +159,7 @@ async def test_existing_email(
headers={"Authorization": f"Bearer {user.id}"},
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
assert after_update.called is False
else:
assert response.status_code == status.HTTP_400_BAD_REQUEST
Expand All @@ -181,7 +181,7 @@ async def test_invalid_password(
headers={"Authorization": f"Bearer {user.id}"},
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
assert after_update.called is False
else:
assert response.status_code == status.HTTP_400_BAD_REQUEST
Expand All @@ -204,7 +204,7 @@ async def test_empty_body(
"/me", json={}, headers={"Authorization": f"Bearer {user.id}"}
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
assert after_update.called is False
else:
assert response.status_code == status.HTTP_200_OK
Expand Down Expand Up @@ -232,7 +232,7 @@ async def test_valid_body(
"/me", json=json, headers={"Authorization": f"Bearer {user.id}"}
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
assert after_update.called is False
else:
assert response.status_code == status.HTTP_200_OK
Expand Down Expand Up @@ -260,7 +260,7 @@ async def test_valid_body_is_superuser(
"/me", json=json, headers={"Authorization": f"Bearer {user.id}"}
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
assert after_update.called is False
else:
assert response.status_code == status.HTTP_200_OK
Expand Down Expand Up @@ -288,7 +288,7 @@ async def test_valid_body_is_active(
"/me", json=json, headers={"Authorization": f"Bearer {user.id}"}
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
assert after_update.called is False
else:
assert response.status_code == status.HTTP_200_OK
Expand Down Expand Up @@ -316,7 +316,7 @@ async def test_valid_body_is_verified(
"/me", json=json, headers={"Authorization": f"Bearer {user.id}"}
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
assert after_update.called is False
else:
assert response.status_code == status.HTTP_200_OK
Expand Down Expand Up @@ -349,7 +349,7 @@ async def test_valid_body_password(
"/me", json=json, headers={"Authorization": f"Bearer {user.id}"}
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
assert after_update.called is False
else:
assert response.status_code == status.HTTP_200_OK
Expand Down Expand Up @@ -535,8 +535,6 @@ async def test_regular_user(
headers={"Authorization": f"Bearer {user.id}"},
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
else:
assert response.status_code == status.HTTP_403_FORBIDDEN

async def test_verified_user(
Expand All @@ -562,7 +560,7 @@ async def test_not_existing_user_unverified_superuser(
headers={"Authorization": f"Bearer {superuser.id}"},
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_404_NOT_FOUND

Expand All @@ -589,7 +587,7 @@ async def test_superuser(
f"/{user.id}", headers={"Authorization": f"Bearer {superuser.id}"}
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_200_OK

Expand Down Expand Up @@ -633,8 +631,6 @@ async def test_regular_user(
headers={"Authorization": f"Bearer {user.id}"},
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
else:
assert response.status_code == status.HTTP_403_FORBIDDEN

async def test_verified_user(
Expand All @@ -661,7 +657,7 @@ async def test_not_existing_user_unverified_superuser(
headers={"Authorization": f"Bearer {superuser.id}"},
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_404_NOT_FOUND

Expand Down Expand Up @@ -689,7 +685,7 @@ async def test_empty_body_unverified_superuser(
f"/{user.id}", json={}, headers={"Authorization": f"Bearer {superuser.id}"}
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_200_OK

Expand Down Expand Up @@ -727,7 +723,7 @@ async def test_valid_body_unverified_superuser(
headers={"Authorization": f"Bearer {superuser.id}"},
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_200_OK

Expand Down Expand Up @@ -808,7 +804,7 @@ async def test_valid_body_is_superuser_unverified_superuser(
headers={"Authorization": f"Bearer {superuser.id}"},
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_200_OK

Expand Down Expand Up @@ -847,7 +843,7 @@ async def test_valid_body_is_active_unverified_superuser(
headers={"Authorization": f"Bearer {superuser.id}"},
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_200_OK

Expand Down Expand Up @@ -886,7 +882,7 @@ async def test_valid_body_is_verified_unverified_superuser(
headers={"Authorization": f"Bearer {superuser.id}"},
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_200_OK

Expand Down Expand Up @@ -930,7 +926,7 @@ async def test_valid_body_password_unverified_superuser(
headers={"Authorization": f"Bearer {superuser.id}"},
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_200_OK
assert mock_user_db.update.called is True
Expand Down Expand Up @@ -982,8 +978,6 @@ async def test_regular_user(
headers={"Authorization": f"Bearer {user.id}"},
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
else:
assert response.status_code == status.HTTP_403_FORBIDDEN

async def test_verified_user(
Expand All @@ -1009,7 +1003,7 @@ async def test_not_existing_user_unverified_superuser(
headers={"Authorization": f"Bearer {superuser.id}"},
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_404_NOT_FOUND

Expand Down Expand Up @@ -1040,7 +1034,7 @@ async def test_unverified_superuser(
f"/{user.id}", headers={"Authorization": f"Bearer {superuser.id}"}
)
if requires_verification:
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.status_code == status.HTTP_403_FORBIDDEN
else:
assert response.status_code == status.HTTP_204_NO_CONTENT
assert response.content == b""
Expand Down

0 comments on commit e59fb2c

Please sign in to comment.