Fastapi Simple Security

Page content

How to Protect your App with Simple Security

Let’s build a small API Endpoint with FastAPI and protect it with SimpleSecurity.

API key based security package for FastAPI, focused on simplicity of use:

  • Full functionality out of the box, no configuration required
  • API key security with local sqlite backend, working with both header and query parameters
  • Default 15 days deprecation for generated API keys
  • Key creation, revocation, renewing, and usage logs handled through administrator endpoints
  • No dependencies, only requiring FastAPI and the python standard library

Build new App

and show the Directory Structure

poetry new demo_fastapi
cd demo_fastapi
user@host:~demo_fastapi> tree
.
├── README.md
├── demo_fastapi
│   └── __init__.py
├── pyproject.toml
└── tests
    └── __init__.py

Add Packages

fastapi, uvicorn, simplesecurity, …

user@host:~demo_fastapi> poetry add fastapi fastapi-simple-security uvicorn
Using version ^0.100.0 for fastapi
Using version ^1.3.0 for fastapi-simple-security
Using version ^0.23.1 for uvicorn

Updating dependencies
Resolving dependencies... (0.7s)

Package operations: 14 installs, 0 updates, 0 removals

  • Installing idna (3.4)
  • Installing sniffio (1.3.0)
  • Installing typing-extensions (4.7.1)
  • Installing annotated-types (0.5.0)
  • Installing anyio (3.7.1)
  • Installing pydantic-core (2.3.0)
  • Installing pydantic (2.0.3)
  • Installing starlette (0.27.0)
  • Installing click (8.1.6)
  • Installing fastapi (0.100.0)
  • Installing h11 (0.14.0)
  • Installing urllib3 (2.0.4)
  • Installing fastapi-simple-security (1.3.0)
  • Installing uvicorn (0.23.1)

Writing lock file

Build Sample Endpoint - main.py

cat <<'EOF'> main.py
from fastapi import FastAPI, Depends

app = FastAPI()


@app.get("/version")
async def version():
    return {"author":"stöge", "version": "v0.01", "status": "open"}
EOF

Run the App

user@host:~demo_fastapi> poetry run uvicorn main:app --reload
INFO:     Will watch for changes in these directories: ['/Users/stoege/git/test/demo_fastapi']
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [13773] using StatReload
INFO:     Started server process [13777]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

and open the Brower http://localhost:8000/version

-> you get:

{“author”:“stöge”,“version”:“v0.01”,“status”:“open”}

Add a bit Security

so far, so good. We have an Endpoint which shows us the Version. Let’s add a bit Security …

Update main.py

cat <<'EOF'> main.py
from fastapi import FastAPI, Depends
from fastapi_simple_security import api_key_router, api_key_security

app = FastAPI()
app.include_router(api_key_router, prefix="/auth", tags=["_auth"])

@app.get("/version")
async def version():
    return {"author":"stöge", "version": "v0.01", "status": "open"}

@app.get("/sversion", dependencies=[Depends(api_key_security)])
async def sversion():
    return {"author":"stöge", "version": "v0.01", "status": "protected"}
EOF

Rerun the App

key=$(uuidgen)
echo "Key: $key"
export FASTAPI_SIMPLE_SECURITY_SECRET="${key}"; poetry run uvicorn main:app --reload

Key: 6D89C1EF-063F-45FC-856E-B7F3F7A896E4

Call /version

same endpoint, same result

user@host:~demo_fastapi> curl -s http://localhost:8000/version |jq
{
  "author": "stöge",
  "version": "v0.01",
  "status": "open"
}

Call /sversion

we got a new endpoint called “/sversion”. Try to call it ..

user@host:~demo_fastapi> curl -s http://localhost:8000/sversion |jq
{
  "detail": "An API key must be passed as query or header"
}

Get New API Key

query_key=$(curl -s -X 'GET' \
  'http://localhost:8000/auth/new?never_expires=false' \
  -H 'accept: application/json' \
  -H 'secret-key: 6D89C1EF-063F-45FC-856E-B7F3F7A896E4' |jq -r '.')
echo $query_key
8ed082ea-57de-4c24-85ba-2c26234f27ac

our key: 8ed082ea-57de-4c24-85ba-2c26234f27ac

Call /sversion again

curl -X 'GET' \
  'http://localhost:8000/sversion?api-key=8ed082ea-57de-4c24-85ba-2c26234f27ac' \
  -H 'accept: application/json'
{
  "author": "stöge",
  "version": "v0.01",
  "status": "protected"
}

here we are …


Any Comments ?

sha256: ee2f4ebbe9178af5e4f6ce8895128696751dca61895413d584dc7005dd74d2aa