Skip to content

Commit

Permalink
♻️ Update example for basic auth to make it more explicit, fix line h…
Browse files Browse the repository at this point in the history
…ighlighting and docs
  • Loading branch information
tiangolo committed Aug 24, 2022
1 parent 8f1340a commit fa8fa26
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 12 deletions.
17 changes: 10 additions & 7 deletions docs/en/docs/advanced/security/http-basic-auth.md
Expand Up @@ -34,13 +34,19 @@ Here's a more complete example.

Use a dependency to check if the username and password are correct.

For this, use the Python standard module <a href="https://docs.python.org/3/library/secrets.html" class="external-link" target="_blank">`secrets`</a> to check the username and password:
For this, use the Python standard module <a href="https://docs.python.org/3/library/secrets.html" class="external-link" target="_blank">`secrets`</a> to check the username and password.

```Python hl_lines="1 11-13"
`secrets.compare_digest()` needs to take `bytes` or a `str` that only contains ASCII characters (the ones in English), this means it wouldn't work with characters like `á`, as in `Sebastián`.

To handle that, we first convert the `username` and `password` to `bytes` encoding them with UTF-8.

Then we can use `secrets.compare_digest()` to ensure that `credentials.username` is `"stanleyjobson"`, and that `credentials.password` is `"swordfish"`.

```Python hl_lines="1 11-21"
{!../../../docs_src/security/tutorial007.py!}
```

This will ensure that `credentials.username` is `"stanleyjobson"`, and that `credentials.password` is `"swordfish"`. This would be similar to:
This would be similar to:

```Python
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
Expand All @@ -50,9 +56,6 @@ if not (credentials.username == "stanleyjobson") or not (credentials.password ==

But by using the `secrets.compare_digest()` it will be secure against a type of attacks called "timing attacks".

!!! tip
We are comparing `bytes` (`encode("idna")`) instead of `str` with `compare_digest` because it cannot handle strings with non-ascii characters.

### Timing Attacks

But what's a "timing attack"?
Expand Down Expand Up @@ -105,6 +108,6 @@ That way, using `secrets.compare_digest()` in your application code, it will be

After detecting that the credentials are incorrect, return an `HTTPException` with a status code 401 (the same returned when no credentials are provided) and add the header `WWW-Authenticate` to make the browser show the login prompt again:

```Python hl_lines="15-19"
```Python hl_lines="23-27"
{!../../../docs_src/security/tutorial007.py!}
```
14 changes: 9 additions & 5 deletions docs_src/security/tutorial007.py
Expand Up @@ -9,13 +9,17 @@


def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
correct_username = secrets.compare_digest(
credentials.username.encode("idna"), "stanleyjobson".encode("idna")
current_username_bytes = credentials.username.encode("utf8")
correct_username_bytes = b"stanleyjobson"
is_correct_username = secrets.compare_digest(
current_username_bytes, correct_username_bytes
)
correct_password = secrets.compare_digest(
credentials.password.encode("idna"), "swordfish".encode("idna")
current_password_bytes = credentials.password.encode("utf8")
correct_password_bytes = b"swordfish"
is_correct_password = secrets.compare_digest(
current_password_bytes, correct_password_bytes
)
if not (correct_username and correct_password):
if not (is_correct_username and is_correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect email or password",
Expand Down

0 comments on commit fa8fa26

Please sign in to comment.