Skip to main content

Problem

Quint’s original authentication used a flat API key scheme (X-API-Key header, SHA-256 hashed). This had no concept of:
  • Agent identity or delegation chains
  • Role-based access control (RBAC)
  • Action signatures / non-repudiation
  • Human override flows for high-risk decisions
  • Token rotation or granular revocation

Solution

A 6-type token hierarchy that traces accountability from humans through agents and sub-agents with cryptographic attestation. The system is split into two components:
  1. Shared auth library (src/auth/) — In-process validation on the API hot path (under 10ms)
  2. Auth service (src/auth_service/) — Separate FastAPI container for token lifecycle management

Architecture

┌─────────────────────────────────────────────────────────┐
│                     Client / Agent                       │
│                                                          │
│   Authorization: Bearer qt_agent_...                     │
│   X-Quint-Session: qt_session_...    (optional)          │
│   X-Action-Signature: ...            (optional)          │
└───────────────────────┬──────────────────────────────────┘


┌─────────────────────────────────────────────────────────┐
│                     API Service (:8000)                   │
│                                                          │
│  ┌──────────────────────────────────────────────────┐    │
│  │             AuthMiddleware (dual-mode)             │    │
│  │                                                    │    │
│  │  Bearer qt_* ──► TokenValidator (in-process)       │    │
│  │  X-API-Key   ──► Legacy SHA-256 lookup             │    │
│  └──────────────────────────────────────────────────┘    │
│                        │                                  │
│                        ▼                                  │
│  ┌──────────────────────────────────────────────────┐    │
│  │          RBAC Enforcement (dependency)             │    │
│  │   check_rbac(policy, event.action, resource)       │    │
│  └──────────────────────────────────────────────────┘    │
│                        │                                  │
│                        ▼                                  │
│               Event Scoring Pipeline                      │
└───────────────────────┬──────────────────────────────────┘

            ┌───────────┼───────────┐
            ▼           ▼           ▼
       ┌─────────┐ ┌─────────┐ ┌─────────┐
       │Postgres │ │  Redis  │ │ Auth Svc │
       │ (auth   │ │ (bloom  │ │ (:8001)  │
       │ schema) │ │ filter) │ │          │
       └─────────┘ └─────────┘ └─────────┘

Design Decisions

In-process validation (not an auth gateway)

Token validation runs inside the API process — no network hop to the auth service. This is critical for the sub-10ms latency target on event ingestion. The auth service is only called for management operations (create, rotate, revoke tokens).

Bloom filter for revocation

Instead of checking the database on every request, a Redis bitmap bloom filter provides O(1) revocation checks with ~0.8% false positive rate. The bloom filter is rebuilt from the auth.revocation_log table on demand.

Dual-mode auth (backward compatible)

Both Authorization: Bearer qt_* and X-API-Key work simultaneously. Legacy responses include a deprecation header. No breaking changes during migration.

ES256 (ECDSA P-256) over RS256

ES256 was chosen over RS256 for:
  • Smaller key sizes (256-bit vs 2048+ bit)
  • Faster signature verification (~0.5ms)
  • Industry alignment with WebAuthn / FIDO2

Permission narrowing

Sub-agent tokens must have RBAC policies that are strict subsets of their parent. An agent cannot delegate more permissions than it holds — enforced at token creation time.

File Layout

src/auth/                  # Shared library (imported by API)
├── constants.py           # Prefixes, TTLs, limits, bloom config
├── models.py              # TokenClaims, RBACPolicy, ValidatedToken
├── jwt.py                 # ES256 encode/decode
├── rbac.py                # RBAC evaluation (deny-first, glob matching)
├── bloom.py               # Redis bitmap bloom filter
├── signatures.py          # HMAC-SHA256 action signatures
├── webhooks.py            # Webhook payload signing
├── validator.py           # TokenValidator pipeline
└── exceptions.py          # Auth-specific errors

src/auth_service/          # Standalone FastAPI service
├── main.py                # App factory
├── config.py              # AuthSettings
├── dependencies.py        # DI: sessions, redis
├── routes/                # REST API
│   ├── tokens.py          # Token CRUD (6 types)
│   ├── keys.py            # Signing key management
│   ├── revocation.py      # Revoke + cascade + bloom rebuild
│   ├── webhooks.py        # Webhook registration
│   └── overrides.py       # Override token decisions
├── services/              # Business logic
│   ├── token_service.py   # Derivation chain enforcement
│   ├── key_service.py     # AES-256-GCM key encryption
│   ├── revocation_service.py
│   ├── signature_service.py
│   ├── override_service.py
│   └── webhook_service.py
└── db/                    # Persistence
    ├── engine.py          # Auth-specific engine
    ├── session.py         # RLS session factory
    ├── models.py          # SQLAlchemy ORM (10 tables)
    └── queries/           # CRUD queries