Skip to main content
This example demonstrates how to accept and manage tokens from different locations:
  • Authorization headers (Bearer tokens)
  • HTTP-only cookies
  • JSON request body
  • Query string parameters
  • Automatic token extraction from the request
  • Cookie management (setting and unsetting)

Complete example

from fastapi import FastAPI, HTTPException, Request, Response
from pydantic import BaseModel

from authx import AuthX, AuthXConfig

# Create a FastAPI app
app = FastAPI(title="AuthX Token Locations Example")

# Configure AuthX
auth_config = AuthXConfig(
    JWT_ALGORITHM="HS256",
    JWT_SECRET_KEY="your-secret-key",  # In production, use a secure key and store it in environment variables
    # Configure token locations
    JWT_TOKEN_LOCATION=["headers", "cookies", "json", "query"],
    # Header settings
    JWT_HEADER_TYPE="Bearer",
    # Cookie settings
    JWT_ACCESS_COOKIE_NAME="access_token_cookie",
    JWT_REFRESH_COOKIE_NAME="refresh_token_cookie",
    JWT_COOKIE_SECURE=False,  # Set to True in production with HTTPS
    JWT_COOKIE_CSRF_PROTECT=False,  # Disable CSRF protection for testing
    JWT_ACCESS_CSRF_COOKIE_NAME="csrf_access_token",
    JWT_REFRESH_CSRF_COOKIE_NAME="csrf_refresh_token",
    JWT_ACCESS_CSRF_HEADER_NAME="X-CSRF-TOKEN-Access",
    JWT_REFRESH_CSRF_HEADER_NAME="X-CSRF-TOKEN-Refresh",
    # JSON body settings
    JWT_JSON_KEY="access_token",
    JWT_REFRESH_JSON_KEY="refresh_token",
    # Query string settings
    JWT_QUERY_STRING_NAME="token",
)

# Initialize AuthX
auth = AuthX(config=auth_config)

# Register error handlers
auth.handle_errors(app)


# Define models
class User(BaseModel):
    username: str
    password: str


class TokenResponse(BaseModel):
    access_token: str
    refresh_token: str
    token_type: str = "bearer"


class TokenBody(BaseModel):
    access_token: str


# Sample user database (in a real app, you would use a database)
USERS = {
    "user1": {"password": "password1", "email": "user1@example.com"},
    "user2": {"password": "password2", "email": "user2@example.com"},
}


@app.post("/login")
def login(user: User, response: Response):
    """Login endpoint that validates credentials and returns tokens."""
    # Check if user exists and password is correct
    if user.username in USERS and USERS[user.username]["password"] == user.password:
        try:
            # Create access and refresh tokens
            access_token = auth.create_access_token(user.username)
            refresh_token = auth.create_refresh_token(user.username)

            # Set tokens in cookies if cookies are enabled
            if "cookies" in auth_config.JWT_TOKEN_LOCATION:
                auth.set_access_cookies(access_token, response)
                auth.set_refresh_cookies(refresh_token, response)

            # Return tokens in response body
            return {
                "access_token": access_token,
                "refresh_token": refresh_token,
                "token_type": "bearer",
                "message": "Tokens are set in cookies and returned in the response body",
            }
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e)) from e

    # Return error if credentials are invalid
    raise HTTPException(status_code=401, detail="Invalid username or password")


@app.post("/logout")
def logout(response: Response):
    """Logout endpoint that clears the cookies."""
    auth.unset_cookies(response)
    return {"message": "Successfully logged out"}


@app.get("/protected")
async def protected_route(request: Request):
    """Protected route that requires a valid access token from any location."""
    try:
        # get and verify the token from the request
        token = await auth.get_access_token_from_request(request)
        payload = auth.verify_token(token, verify_csrf=False)

        return {"message": "Access granted", "username": payload.sub, "token_location": token.location}
    except Exception as e:
        raise HTTPException(status_code=401, detail=str(e)) from e


@app.post("/protected-post")
async def protected_post_route(request: Request, token_body: TokenBody = None):
    """Protected route that requires a valid access token from any location."""
    try:
        # Get and verify the token from the request
        token = await auth.get_access_token_from_request(request)
        payload = auth.verify_token(token)

        return {"message": "Access granted", "username": payload.sub, "token_location": token.location}
    except Exception as e:
        raise HTTPException(status_code=401, detail=str(e)) from e


@app.get("/protected/headers")
async def protected_headers(request: Request):
    """Protected route that expects the token in the Authorization header."""
    try:
        # Get and verify the token from the request
        token = await auth.get_access_token_from_request(request)
        payload = auth.verify_token(token)

        return {
            "message": "Access granted via Authorization header",
            "username": payload.sub,
            "token_location": token.location,
        }
    except Exception as e:
        raise HTTPException(status_code=401, detail=str(e)) from e


@app.get("/protected/cookies")
async def protected_cookies(request: Request):
    """Protected route that expects the token in cookies."""
    try:
        # Get and verify the token from the request
        token = await auth.get_access_token_from_request(request)
        payload = auth.verify_token(token)

        return {"message": "Access granted via cookies", "username": payload.sub, "token_location": token.location}
    except Exception as e:
        raise HTTPException(status_code=401, detail=str(e)) from e


@app.post("/protected/json")
async def protected_json(request: Request, token_body: TokenBody = None):
    """Protected route that expects the token in the JSON body."""
    try:
        # Get and verify the token from the request
        token = await auth.get_access_token_from_request(request)
        payload = auth.verify_token(token)

        return {"message": "Access granted via JSON body", "username": payload.sub, "token_location": token.location}
    except Exception as e:
        raise HTTPException(status_code=401, detail=str(e)) from e


@app.get("/protected/query")
async def protected_query(request: Request):
    """Protected route that expects the token in the query string."""
    try:
        # Get and verify the token from the request
        token = await auth.get_access_token_from_request(request)
        payload = auth.verify_token(token)

        return {"message": "Access granted via query string", "username": payload.sub, "token_location": token.location}
    except Exception as e:
        raise HTTPException(status_code=401, detail=str(e)) from e


@app.get("/")
def read_root():
    """Public route that doesn't require authentication."""
    return {
        "message": "Welcome to AuthX Token Locations Example",
        "endpoints": {
            "login": "POST /login - Get tokens in cookies and response body",
            "logout": "POST /logout - Clear the cookies",
            "protected": "GET /protected - Access with token from any location",
            "protected-post": "POST /protected-post - Access with token from any location",
            "protected/headers": "GET /protected/headers - Access with token in Authorization header",
            "protected/cookies": "GET /protected/cookies - Access with token in cookies",
            "protected/json": "POST /protected/json - Access with token in JSON body",
            "protected/query": "GET /protected/query?token=<your_token> - Access with token in query string",
        },
    }


if __name__ == "__main__":
    import os

    import uvicorn

    port = int(os.environ.get("PORT", 8000))
    uvicorn.run(app, host="0.0.0.0", port=port)

How it works

1

Configure multiple locations

Set JWT_TOKEN_LOCATION to a list of locations where AuthX should look for tokens: ["headers", "cookies", "json", "query"].
2

Configure location-specific settings

For each location, configure the relevant settings (cookie names, header type, JSON keys, query parameter names).
3

Extract tokens automatically

Use auth.get_access_token_from_request() to automatically extract the token from any configured location.
4

Set cookies on login

Use auth.set_access_cookies() and auth.set_refresh_cookies() to store tokens in HTTP-only cookies.

Token location methods

Send tokens in the Authorization header with Bearer scheme:
curl http://localhost:8000/protected \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc..."
Configuration:
JWT_HEADER_NAME="Authorization"
JWT_HEADER_TYPE="Bearer"

Running the example

1

Install dependencies

pip install authx-python fastapi uvicorn
2

Run the server

python token_locations.py
3

Login to get tokens

curl -X POST http://localhost:8000/login \
  -H "Content-Type: application/json" \
  -d '{"username":"user1","password":"password1"}' \
  -c cookies.txt
This saves cookies to cookies.txt and returns tokens in the response body.
4

Test different token locations

# Via Authorization header
curl http://localhost:8000/protected \
  -H "Authorization: Bearer <access_token>"

# Via cookies
curl http://localhost:8000/protected \
  -b cookies.txt

# Via JSON body
curl -X POST http://localhost:8000/protected/json \
  -H "Content-Type: application/json" \
  -d '{"access_token":"<access_token>"}'

# Via query string
curl "http://localhost:8000/protected/query?token=<access_token>"

Choosing the right location

  • Headers: Best for API clients, mobile apps, and SPAs. Standard and secure.
  • Cookies: Best for traditional web apps. Enables HTTP-only storage and automatic sending.
  • JSON body: Useful for custom authentication flows or when headers aren’t easily accessible.
  • Query strings: Use sparingly, only for scenarios like WebSocket connections or embedded images.
You can enable multiple locations simultaneously. AuthX will check each location in order and use the first valid token found.
For production cookie-based authentication, use these settings:
auth_config = AuthXConfig(
    JWT_COOKIE_SECURE=True,        # Only send over HTTPS
    JWT_COOKIE_HTTP_ONLY=True,     # Prevent JavaScript access
    JWT_COOKIE_SAMESITE="strict",  # CSRF protection
    JWT_COOKIE_CSRF_PROTECT=True,  # Additional CSRF protection
)

Build docs developers (and LLMs) love