Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

fleet-auth

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fleet-auth

Authentication middleware for SuperInstance fleet Cloudflare Workers

latest
npmnpm
Version
1.0.0
Version published
Maintainers
1
Created
Source

fleet-auth

Authentication middleware for the SuperInstance fleet — API keys, JWTs, and service bindings, purpose-built for Cloudflare Workers.

Every fleet worker needs to answer the same question: who is calling, and are they allowed to? This crate answers it. It provides a single withAuth() middleware that handles API keys (fleet_sk_...), HS256 JWTs, and Cloudflare service-binding detection — so your worker handler only sees authenticated requests with granted scopes.

Why This Exists

The SuperInstance fleet has 20+ Cloudflare Workers communicating over HTTP. Every worker needs auth, but every worker shouldn't reimplement it. fleet-auth is the shared authentication layer that:

  • Validates credentials — API keys (hashed, stored in D1, mirrored to KV) and JWTs (signed with rotating secrets)
  • Enforces scopes — fine-grained permissions like read:vector, write:fishinglog, or wildcard *
  • Audits every request — logs auth outcomes to D1 without blocking the response
  • Detects service bindings — Cloudflare-internal requests are trusted automatically (the runtime guarantees authenticity)

The design targets sub-5ms auth overhead. KV lookups for API keys hit in 1–3ms. JWT verification is pure CPU (no I/O). Service binding detection is a single header check.

Architecture

                        Incoming Request
                              │
                    ┌─────────▼─────────┐
                    │   withAuth()      │
                    │   middleware      │
                    └─────────┬─────────┘
                              │
                  ┌───────────▼───────────┐
                  │ Service binding?      │──── Yes ──► Trust (CF runtime guarantees auth)
                  │ (CF-Connecting-Source)│
                  └───────────┬───────────┘
                              │ No
                  ┌───────────▼───────────┐
                  │ Extract Bearer token  │
                  └───────────┬───────────┘
                              │
              ┌───────────────┼───────────────┐
              │               │               │
    ┌─────────▼──────┐  ┌────▼─────┐  ┌──────▼──────┐
    │ fleet_sk_...   │  │ JWT      │  │ Unknown     │
    │ (API key path) │  │ (3-part) │  │             │
    └─────────┬──────┘  └────┬─────┘  └──────┬──────┘
              │               │               │
    ┌─────────▼──────┐  ┌────▼─────┐          │
    │ KV lookup      │  │ HS256    │          │
    │ (fallback D1)  │  │ verify   │          │
    │ SHA-256 compare│  │ (rotate) │          │
    └─────────┬──────┘  └────┬─────┘          │
              │               │               │
              └───────┬───────┘       ┌────────▼────────┐
                      │               │ 401 Unauthorized │
              ┌───────▼───────┐       └─────────────────┘
              │ Scope check   │
              │ (wildcard ok) │
              └───────┬───────┘
                      │
           ┌──────────▼──────────┐
           │ Your handler(req,   │
           │ env, ctx, auth)     │
           └─────────────────────┘

Key Design Decisions

DecisionRationale
Dual auth (API key + JWT)API keys for long-lived service accounts; JWTs for short-lived user sessions
SHA-256 hashed keysNever store plaintext secrets. Keys are hashed on creation, compared by hash on validation.
KV mirror + D1 source of truthKV for hot-path latency (1–3ms); D1 for durability and complex queries
JWT secret rotationJWT_SECRET_CURRENT + JWT_SECRET_PREVIOUS enables zero-downtime key rotation
Audit to D1 (non-blocking)ctx.waitUntil() ensures audit logging never blocks the response
Service binding trustCF runtime authenticates internal requests — no token needed

Quick Start

Install as a dependency

# In your Cloudflare Worker project
cp -r ../fleet-auth/src/auth ./src/auth
# Or import directly if published

Protect an endpoint

import { withAuth } from "./auth/middleware";

export default {
  fetch: withAuth(["read:vector"], async (req, env, ctx, auth) => {
    // auth.keyId  — who's calling
    // auth.scopes — what they can do
    // auth.via    — "api-key" | "jwt" | "service-binding"
    return Response.json({ ok: true, you: auth.keyId });
  }),
};

Generate an API key

import { generateApiKey } from "./auth/keys";

const result = await generateApiKey(env, {
  name: "fleet-scanner-service",
  owner: "ci-pipeline",
  scopes: ["read:vector", "read:fleet"],
  expiresIn: 30 * 24 * 60 * 60 * 1000, // 30 days
});

console.log("Key (save this — shown once):", result.key);
console.log("Key ID:", result.keyId);
// Key format: fleet_sk_<8-char-base32-id>.<44-char-base64url-secret>

Create a JWT

import { sign } from "./auth/jwt";

const token = await sign(
  {
    iss: "fleet-auth",
    sub: "user-123",
    aud: "fleet-vector-api",
    scopes: ["read:vector", "write:fishinglog"],
    kid: "key-1",
  },
  env.JWT_SECRET_CURRENT,
);
// token is a standard HS256 JWT, expires in 15 minutes

API Reference

Middleware

withAuth(requiredScopes, handler, options?)

Wrap a Workers fetch handler with authentication.

withAuth(
  ["read:vector", "write:fishinglog"],  // all must be present
  async (req, env, ctx, auth) => { /* your handler */ },
  { allowServiceBinding: true, skipAudit: false }
);

hasScopes(granted, required)

Check scope satisfaction. Supports wildcards:

  • "*" matches everything
  • "read:*" matches "read:vector", "read:fleet", etc.

API Key Management

FunctionDescription
generateApiKey(env, options)Create key, store hash in D1, mirror to KV. Returns full key (once!).
validateApiKey(token, env)KV-first lookup, D1 fallback. SHA-256 comparison.
rotateApiKey(env, oldKeyId, options)Create replacement key. Old key stays valid during grace period.
revokeApiKey(env, keyId)Immediate revocation. Deletes from KV, marks revoked in D1.

JWT Utilities

FunctionDescription
sign(payload, secret)Sign a JWT with HS256. 15-minute TTL.
verify(token, secrets)Verify against current + previous secrets (rotation support).
decode(token)Decode without verifying (debugging only).
shouldRenew(token, withinSeconds?)Check if token expires soon. Default: within 120s.

API Key Format

fleet_sk_abcdefghij.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
         │       │ ──────────────────────────────────────────────
         │       │              32-byte random secret (base64url)
         │       └── 8-char base32 key ID (used for lookup)
         └── prefix

The key ID (abcdefghij) is the public identifier. It's used for KV lookups, D1 queries, and audit logs. The secret is the private half — it's stored as a SHA-256 hash, never in plaintext. When you validate, you hash the submitted secret and compare.

This is the same pattern as Stripe API keys (sk_live_...) and GitHub tokens (ghp_...).

Scopes

Scopes follow the verb:resource pattern:

ScopeDescription
read:vectorRead vector embeddings
write:fishinglogWrite fishing log entries
read:fleetRead fleet status
*Full access (service bindings get this)
read:*Read access to all resources

Env Bindings

Your Worker needs these bindings in wrangler.toml:

[[d1_databases]]
binding = "DB"
database_name = "fleet-auth-db"
database_id = "your-database-id"

[[kv_namespaces]]
binding = "AUTH_KV"
id = "your-kv-namespace-id"

[vars]
WORKER_NAME = "your-worker-name"

# Set via `wrangler secret put`:
# JWT_SECRET_CURRENT — current signing secret
# JWT_SECRET_PREVIOUS — previous secret (for rotation)

D1 Schema

The api_keys and auth_audit tables must exist. Deploy from fleet-events-db migrations:

-- api_keys table
CREATE TABLE api_keys (
  key_id TEXT PRIMARY KEY,
  key_hash TEXT NOT NULL,
  name TEXT NOT NULL,
  owner TEXT NOT NULL,
  scopes TEXT NOT NULL,      -- JSON array
  status TEXT NOT NULL,       -- 'active', 'rotating', 'revoked'
  created_at INTEGER NOT NULL,
  expires_at INTEGER,
  last_used_at INTEGER,
  rotated_from TEXT
);

-- auth_audit table
CREATE TABLE auth_audit (
  ts INTEGER NOT NULL,
  key_id TEXT,
  worker TEXT NOT NULL,
  path TEXT NOT NULL,
  outcome TEXT NOT NULL,     -- 'ok', 'invalid', 'expired', 'revoked', 'scope_denied', 'malformed'
  ip TEXT
);

JWT Rotation

Zero-downtime secret rotation:

  • Set JWT_SECRET_PREVIOUS = current_secret
  • Set JWT_SECRET_CURRENT = new_secret
  • Tokens signed with the old secret still validate (via PREVIOUS)
  • New tokens are signed with the new secret
  • After all old tokens expire (15 minutes), remove JWT_SECRET_PREVIOUS

Conservation Law Connection

Every authenticated request follows the fleet's conservation law (γ + η = C):

  • One request inone audit record out (no request goes unlogged)
  • One API key createdone hash stored (no duplication)
  • One JWT issuedone JWT verifiable (no drift)

The audit trail is append-only, like the fleet's event sourcing in fleet-events-db. Auth events are first-class fleet events.

License

MIT

FAQs

Package last updated on 11 Jun 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts