# API keys and Basic

## API keys

`APIKeyHeaderAuth` reads a named header:

```python
from fastapi import FastAPI, Security

from fastapi_multiauth import APIKeyHeaderAuth, UnauthorizedError, hash_token

CLIENTS = {hash_token("key-for-reporting"): "reporting-service"}


async def validate_api_key(key: str) -> str:
    client = CLIENTS.get(hash_token(key))
    if client is None:
        raise UnauthorizedError()
    return client


api_key = APIKeyHeaderAuth(name="X-API-Key", validator=validate_api_key)

app = FastAPI()


@app.get("/reports")
async def reports(client: str = Security(api_key)):
    return {"client": client}
```

Generate and store keys like [bearer tokens](<https://fastapi-multiauth.d3vyce.fr/guide/bearer-tokens/index.md>): random, and hashed at rest.

`APIKeyQueryAuth` is the same for a query parameter (`?api_key=...`). Prefer the header: query strings end up in logs and browser history.

## Basic auth

`HTTPBasicAuth` calls your validator with `username, password`:

```python
import os
import secrets

from fastapi import FastAPI, Security

from fastapi_multiauth import HTTPBasicAuth, UnauthorizedError

METRICS_PASSWORD = os.environ["METRICS_PASSWORD"]


async def validate_basic(username: str, password: str) -> str:
    username_ok = secrets.compare_digest(username.encode(), b"prometheus")
    password_ok = secrets.compare_digest(password.encode(), METRICS_PASSWORD.encode())
    if not (username_ok and password_ok):
        raise UnauthorizedError()
    return username


basic = HTTPBasicAuth(validator=validate_basic, realm="metrics")

app = FastAPI()


@app.get("/metrics")
async def metrics(username: str = Security(basic)):
    return {"scraped_by": username}
```

`realm` is sent in the `WWW-Authenticate` header, which makes browsers show a login prompt. Compare secrets with `secrets.compare_digest`, never `==`, and compare bytes: it raises `TypeError` on non-ASCII strings. Check the username and the password both, so the response time does not reveal which one was wrong.
