CRUMB a card from devarno-cloud

Multi-Strategy Authentication

smo1 advanced 10 min read

ELI5

purr-api accepts identity proof in five different formats, like a nightclub that accepts a passport, a driver’s licence, a membership card, a VIP wristband, or a text from the owner. The bouncer (auth middleware) checks each format in order until one works. If none work and the door requires ID, you are turned away. If the door is optional-ID, you can enter as a guest.

Technical Deep Dive

Auth Strategy Priority

%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#e8f4f8', 'primaryTextColor': '#2d3748', 'primaryBorderColor': '#90cdf4', 'lineColor': '#718096', 'secondaryColor': '#f0fff4', 'tertiaryColor': '#fefcbf'}}}%%
flowchart TD
A[Incoming Request] --> B{Header:<br/>X-API-Key ?}
B -->|Yes| C[Strategy 1:<br/>API Key]
B -->|No| D{Header:<br/>Authorization: Bearer ?}
D -->|Yes| E[Strategy 2:<br/>JWT Bearer]
D -->|No| F{Cookie:<br/>airlock JWT ?}
F -->|Yes| G[Strategy 3:<br/>Airlock RS256 JWT]
F -->|No| H{Cookie:<br/>smo1_session ?}
H -->|Yes| I[Strategy 4:<br/>Session Cookie]
H -->|No| J{Header:<br/>X-Internal-Key ?}
J -->|Yes| K[Strategy 5:<br/>Internal Key]
J -->|No| L[No auth context]
C --> M{Valid ?}
E --> M
G --> M
I --> M
K --> M
M -->|Yes| N[Set user context<br/>proceed to handler]
M -->|No| O{OptionalAuth ?}
O -->|Yes| L
O -->|No| P[Return 401]

Strategy Details

#StrategyIdentifierVerificationUse case
1API KeyX-API-Key: sk_...SHA-256 hash lookup in DB; check revocation/expirationThird-party integrations, scripts
2JWT BearerAuthorization: Bearer <jwt>HMAC-SHA256 with JWTSecret; extract sub claimMachine-to-machine, mobile apps
3Airlock JWTCookie from airlock OIDCRS256 verification against JWKS endpoint; validate issuerWeb dashboard (meow-web)
4Session Cookiesmo1_session or __Secure-smo1.session_tokenForward to meow-web /api/auth/session; verify HMACLegacy BetterAuth compatibility
5Internal API KeyX-Internal-KeySimple string match against env varService-to-service (meow-web → purr-api)

API Key Strategy

  1. Extract header X-API-Key
  2. Compute SHA-256 hash of the provided key
  3. Query api_keys table: SELECT * FROM api_keys WHERE key_hash = $1 AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at > NOW())
  4. If found, set user context from user_id
  5. Async: update last_used_at timestamp (non-blocking)

Security note: The plaintext key is shown exactly once at creation. Only the hash is stored.

JWT Bearer Strategy

  1. Extract Authorization: Bearer <token>
  2. Parse JWT without verification first (to read alg header)
  3. Verify signature with JWTSecret using HMAC-SHA256
  4. Validate exp claim
  5. Extract sub claim as user ID
  6. Lookup user in PostgreSQL

Airlock JWT Strategy (RS256)

  1. Read airlock JWT from cookie (name varies by environment)
  2. Fetch JWKS from airlock.devarno.cloud (cached for 5 minutes)
  3. Verify signature with Ed25519/RS256 public key from JWKS
  4. Validate iss, aud, exp claims
  5. Extract sub (user ID), email, name
  6. Auto-create user if not exists in PostgreSQL (idempotent)
  1. Read smo1_session or __Secure-smo1.session_token cookie
  2. Forward to meow-web GET /api/auth/session with the cookie
  3. meow-web verifies HMAC-SHA256 signature and expiry internally
  4. Returns user data; purr-api trusts this response
  5. Fallback: check for BetterAuth legacy session

Internal API Key

  1. Read X-Internal-Key header
  2. Compare against INTERNAL_API_KEY environment variable
  3. If match, set a synthetic “internal service” user context
  4. No user-specific permissions — full internal access

OptionalAuth vs. RequireAuth

MiddlewareBehaviourExample routes
OptionalAuthExtracts user context if present; allows anonymousPublic link info, landing page data
RequireAuthReturns 401 if no valid authDashboard, link CRUD, billing

Rate Limiting Context

Auth middleware sets the rate-limit identifier before the rate limiter runs:

  • Authenticated requests: user:{user_id}
  • API key requests: apikey:{api_key_id}
  • Anonymous requests: ip:{ip_address}

This ensures authenticated users get their tier’s higher limit, while anonymous requests share a lower per-IP cap.

Key Terms

  • JWKS → JSON Web Key Set; a JSON document containing public keys for JWT verification, fetched from the OIDC provider
  • RS256 → RSA-SHA256 asymmetric signing; private key signs, public key verifies (used by Airlock)
  • HMAC-SHA256 → Symmetric signing; same secret signs and verifies (used by internal JWT and session cookies)
  • Auto-create user → On first Airlock login, purr-api creates a User row automatically to avoid manual onboarding
  • Key prefix → First 8 characters of an API key plaintext, stored for UI display (sk_live_...)

Q&A

Q: Why five strategies instead of just one? A: Different clients have different capabilities. Browsers use cookies. Scripts use API keys. Mobile apps may use JWT. Internal services use a shared secret. Supporting all five minimizes integration friction.

Q: What prevents someone from forging an API key? A: Only the SHA-256 hash is stored. To forge a key, an attacker must find a preimage for a known hash (computationally infeasible for SHA-256). Key generation uses crypto/rand for 32+ bytes of entropy.

Q: Why does the Airlock strategy auto-create users? A: Airlock is the canonical identity provider. If Airlock says a user exists, purr-api trusts that and provisions a local record. This eliminates manual user registration and keeps identity state in one place.

Q: Can a request pass multiple auth checks simultaneously? A: Yes, but only the first valid strategy is used. If a request has both an API key and a session cookie, the API key wins (higher priority in the chain).

Examples

Think of the API as a high-security building with multiple entrances:

  • API Key is a keycard issued to contractors — it opens specific doors, can be revoked remotely, and logs every use
  • JWT Bearer is a temporary QR code sent to your phone — it expires after a set time and cannot be reused if stolen
  • Airlock JWT is the main employee ID badge — it is issued by the central HR system (Airlock) and works at all company buildings
  • Session Cookie is a guest pass — the front desk (meow-web) vouches for you, and the security guard (purr-api) trusts the front desk
  • Internal Key is the master key held by building maintenance — it overrides everything but is never given to outsiders

neighbors on the map