Fastapi Simple Security
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