Skip to main content

Async SDK for issuing and validating scoped API keys

Project description

apikeys-platform

Async Python SDK for issuing, scoping, and validating API keys — backed by SQLite or PostgreSQL.

Designed for developers who want to add API key management to their own product without building the infrastructure from scratch.

Installation

# SQLite (development / small deployments)
pip install "apikeys-platform[sqlite]"

# PostgreSQL (production)
pip install "apikeys-platform[postgresql]"

# MySQL (production)
pip install "apikeys-platform[mysql]"

# All drivers
pip install "apikeys-platform[all]"

Requires Python 3.11+.

Quickstart

create_tables and APIKeyClient both accept plain URLs — no need to specify the async driver suffix:

import asyncio
from apikeys import APIKeyClient, KeyMetadata, create_tables

async def main():
    # SQLite for dev:   "sqlite:///myapp.db"
    # PostgreSQL prod:  "postgresql://user:pass@host:5432/dbname"
    # MySQL prod:       "mysql://user:pass@host:3306/dbname"
    db_url = "sqlite:///myapp.db"
    await create_tables(db_url)   # creates tables on first run; safe to call on every restart
    client = APIKeyClient(db_url)

    # One-time setup
    org     = await client.create_organization("Acme Inc")
    product = await client.create_product(str(org.id), "My API")
    project = await client.create_project(str(org.id), "v1")
    await client.add_product_to_project(str(product.id), str(project.id))

    # Issue a key for one of your users
    result = await client.create_key(
        str(org.id),
        project_id=str(project.id),
        product_id=str(product.id),
        metadata=KeyMetadata(
            name="Alice's key",
            scopes=["read", "write"],
            rate_limit=1000,
            custom={"user_id": "u_alice", "plan": "pro"},
        ),
    )
    print(result.plaintext)   # return this once to your user

    # Validate an incoming request
    key = await client.validate_key(
        result.plaintext,
        product_id=str(product.id),
        required_scope="read",
    )
    print(key.metadata.custom["user_id"])   # → u_alice

asyncio.run(main())

Key concepts

Concept Description
Organization Top-level tenant — maps to one of your customers or your own company
Project Groups keys within an org (e.g. v1, v2, mobile)
Product A named API surface linked to projects; keys can be scoped to one product
KeyMetadata.custom Arbitrary JSON attached to a key — store user_id, plan, tenant_id, etc.

Keys are scoped from broad → narrow: org-wide → project-scoped → product-scoped.
validate_key() checks the key is active, matches the expected product, and holds the required scope.

Exceptions

from apikeys import InvalidKeyError, RevokedKeyError, InsufficientScopeError
Exception When raised
InvalidKeyError Key not found
RevokedKeyError Key exists but has been revoked
InsufficientScopeError Key is valid but missing the required scope

Full example

End-to-end script showing one-time setup, per-user key creation, request validation, and listing keys by user:

import asyncio
from apikeys import APIKeyClient, KeyMetadata
from apikeys.db.session import create_tables

async def main() -> None:
    DB_URL = "sqlite+aiosqlite:///acme_demo.db"
    await create_tables(DB_URL)
    client = APIKeyClient(DB_URL)

    # ── One-time setup ───────────────────────────────────────────────────────
    org     = await client.create_organization("Acme Corp")
    product = await client.create_product(str(org.id), "Acme App")
    project = await client.create_project(str(org.id), "Acme API v1")
    await client.add_product_to_project(str(product.id), str(project.id))

    ORG_ID     = str(org.id)
    PRODUCT_ID = str(product.id)
    PROJECT_ID = str(project.id)

    # ── Issue a key for a user ───────────────────────────────────────────────
    result = await client.create_key(
        ORG_ID,
        project_id=PROJECT_ID,
        product_id=PRODUCT_ID,
        metadata=KeyMetadata(
            name="Alice's production key",
            scopes=["read", "write"],
            rate_limit=1000,
            custom={"user_id": "u_alice", "plan": "pro"},
        ),
    )
    print(f"plaintext: {result.plaintext}")  # return this ONCE to the user

    # ── Validate an incoming request ─────────────────────────────────────────
    key = await client.validate_key(
        result.plaintext,
        product_id=PRODUCT_ID,
        required_scope="read",
    )
    print(f"user: {key.metadata.custom['user_id']}  plan: {key.metadata.custom['plan']}")

    # ── List a user's keys ───────────────────────────────────────────────────
    all_keys   = await client.list_project_keys(PROJECT_ID)
    alice_keys = [k for k in all_keys if k.metadata.custom.get("user_id") == "u_alice"]
    for k in alice_keys:
        print(f"  {k.metadata.name}  [{k.id}]  revoked={k.revoked_at is not None}")

asyncio.run(main())

Source: examples/acme_integration.py

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

apikeys_platform-0.1.2.tar.gz (9.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

apikeys_platform-0.1.2-py3-none-any.whl (9.2 kB view details)

Uploaded Python 3

File details

Details for the file apikeys_platform-0.1.2.tar.gz.

File metadata

  • Download URL: apikeys_platform-0.1.2.tar.gz
  • Upload date:
  • Size: 9.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.8

File hashes

Hashes for apikeys_platform-0.1.2.tar.gz
Algorithm Hash digest
SHA256 7cd7b9f6115d810dbda8021adb7ef409321e87768ee54828c62b229390ac935f
MD5 77fe7cabbdcdcc1d1fee5afc2379c449
BLAKE2b-256 4207259a62af94235f4a73f9805175aa85ffefc42bff512adf7f9e8d646678ed

See more details on using hashes here.

File details

Details for the file apikeys_platform-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for apikeys_platform-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 993cb19dfb3d62c5d6727497351baffba3ca8ebe2d3699d3212cc139bb0c50fc
MD5 72c2649ad7c46910fc1ad2c122772eb5
BLAKE2b-256 09142244e6f5305c420199839e56fcf4741639962bf5763da7955131326a171d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page