Skip to content

Quart Integration

keyshield supports Quart — the async-native reimplementation of Flask — via keyshield.quart_api.

The integration exposes two helpers:

Helper Purpose
create_api_keys_blueprint Full CRUD management Blueprint
require_api_key Async decorator that verifies Authorization: Bearer <key>

Installation

uv add keyshield[quart]

Quick start

from quart import Quart
from keyshield.quart_api import create_api_keys_blueprint, require_api_key
from keyshield.services.base import ApiKeyService
from keyshield.repositories.in_memory import InMemoryApiKeyRepository
from keyshield.hasher.argon2 import Argon2ApiKeyHasher

_svc = ApiKeyService(
    repo=InMemoryApiKeyRepository(),
    hasher=Argon2ApiKeyHasher(pepper="your-secret-pepper"),
)

async def get_service() -> ApiKeyService:
    return _svc

app = Quart(__name__)
app.register_blueprint(create_api_keys_blueprint(svc_factory=get_service))

Protecting routes

from quart import g

@app.get("/protected")
@require_api_key(svc_factory=get_service)
async def protected():
    key = g.api_key   # verified ApiKey entity
    return {"key_id": key.key_id}

Scope-restricted routes

@app.get("/admin")
@require_api_key(svc_factory=get_service, required_scopes=["admin"])
async def admin():
    return {"admin": True}

Management endpoints

The Blueprint registered by create_api_keys_blueprint mounts these routes (prefix defaults to /api-keys):

Method Path Description
POST /api-keys/ Create a new key (returns plaintext once)
GET /api-keys/ List keys (paginated with ?offset= / ?limit=)
GET /api-keys/<id> Get a key by ID
PATCH /api-keys/<id> Partially update a key
DELETE /api-keys/<id> Delete a key
POST /api-keys/<id>/activate Activate a key
POST /api-keys/<id>/deactivate Deactivate a key
POST /api-keys/search Search with filters
POST /api-keys/count Count keys matching a filter
POST /api-keys/verify Verify a key

SQLAlchemy repository

from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from keyshield.repositories.sql import SqlAlchemyApiKeyRepository

engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
SessionLocal = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

async def get_service() -> ApiKeyService:
    async with SessionLocal() as session:
        async with session.begin():
            repo = SqlAlchemyApiKeyRepository(session)
            return ApiKeyService(repo=repo, hasher=Argon2ApiKeyHasher(pepper="pepper"))

Caching

from keyshield.services.cached import CachedApiKeyService

async def get_service() -> CachedApiKeyService:
    return CachedApiKeyService(
        repo=InMemoryApiKeyRepository(),
        hasher=Argon2ApiKeyHasher(pepper="pepper"),
        cache_ttl=300,
    )

Dev mode (.env keys)

import os
os.environ["API_KEY_DEV"] = "ak_v1-mydevkeyid-mysecret64chars"

async def get_service() -> ApiKeyService:
    svc = ApiKeyService(repo=InMemoryApiKeyRepository(), hasher=Argon2ApiKeyHasher())
    await svc.load_dotenv(envvar_prefix="API_KEY_")
    return svc

Custom URL prefix

app.register_blueprint(
    create_api_keys_blueprint(svc_factory=get_service, url_prefix="/v1/keys")
)