From 9c6c937c2f7a90295840196f450ad32bf3c1bb2e Mon Sep 17 00:00:00 2001 From: Yasser Tahiri Date: Wed, 17 Jan 2024 00:09:53 +0100 Subject: [PATCH] :memo: Add authentication and protected routes --- docs/get-started/basic-usage.md | 93 +++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/docs/get-started/basic-usage.md b/docs/get-started/basic-usage.md index 1e97f2e5..04634759 100644 --- a/docs/get-started/basic-usage.md +++ b/docs/get-started/basic-usage.md @@ -94,3 +94,96 @@ security = AuthX(config=config) !!! info "Note on Algorithm" For demonstration ease, we use a **symmetric** algorithm. Note that an **asymmetric** algorithm offers additional layers of protection. `"RS256"` is the recommended algorithm when signing JWTs. + +### Create `AuthX` instance + +You can now instantiate the `AuthX` object with the your configuration + +```py hl_lines="2 10" +from fastapi import FastAPI +from authx import AuthX, AuthXConfig + +app = FastAPI(title="My Base App") + +config = AuthXConfig() +config.JWT_ALGORITHM = "HS256" +config.JWT_SECRET_KEY = "SECRET_KEY" + +security = AuthX(config=config) +``` + +!!! tip "Loading Configuration after `AuthX.__init__`" + You can also load the configuration after the `AuthX` object is created. This is useful when you want to use the same `AuthX` object for multiple FastAPI applications. + + ```py + config = AuthX() + config.JWT_SECRET_KEY = "SECRET_KEY" + + security = AuthX() + security.load_config(config) + ``` + +## Authentication + +### Create the access token + +To authenticate a user, create a `/login` route in the usual way with FastAPI. + +```py hl_lines="4" +@app.get('/login') +def login(username: str, password: str): + if username == "test" and password == "test": + token = security.create_access_token(uid=username) + return {"access_token": token} + raise HTTPException(401, detail={"message": "Bad credentials"}) +``` + +Once a user has provided valid credentials, use the `AuthX.create_access_token` method to generate a signed token. To associate the user with the token, utilize the `uid` argument. + +!!! info "Note on Privacy" + Avoid including personally identifiable information (PIDs) in the JWT since its content is fully readable. As a best practice, `uid` should typically be a user database index (not ordered). Consider using UUIDs for additional privacy. + +!!! info "Note on Login Protection" + The `/login` route above serves as a simple example. **Avoid passing credentials through query parameters** for security reasons. Implement thorough authentication logic to ensure a more robust login process. + +=== "Request Access Token" + + ```sh + $ curl -s -X POST http://0.0.0.0:8000/login?username=test&password=test + {"access_token": $TOKEN} + ``` + +### Protected Routes + +Let's implement a simple `GET` route that can only be accessed by authenticated users. + +```py +@app.get("/protected", dependencies=[Depends(security.access_token_required)]) +def get_protected(): + return {"message": "Hello World"} +``` + +AuthX is compliant with FastAPI's [dependency injection system](https://fastapi.tiangolo.com/tutorial/dependencies/). It provides the `AuthX.access_token_required` method to enforce this behavior. + +Whether a bad token or no token is provided, the server will prevent the execution of the route logic defined in `/protected`. + +=== "curl without JsonWebToken" + ```bash + $ curl -s http://0.0.0.0:8000/protected + {"detail":"Missing JWT in request"} + ``` + +=== "With a bad JsonWebToken" + ```bash + $ curl -s --oauth2-bearer "dummytoken" http://0.0.0.0:8000/protected + {"detail":"Unauthorized"} + ``` + +=== "With a valid JsonWebToken" + ```bash + $ curl -s --oauth2-bearer $TOKEN http://0.0.0.0:8000/protected + {"message": "Hello World"} + ``` + +!!! failure "Default Exception Behavior" + In the curl requests above, a `401` HTTP Error is raised when the token is not valid. By default, AuthX triggers a `500 Internal Server Error` HTTP Error. For the sake of simplicity, we won't delve into error handling in this section.