jampress.pw
Philosophy Docs Roadmap Playground GitHub

Documentation

Philosophy Three tracks, modular scaling, and the foundation rules that shape every package. Architecture Stable viewer contracts with swappable provider/storage internals. Security Threat model, token/session controls, and rate-limit requirements. Roadmap Phase-by-phase path from DIY auth to WordPress bridge and adapters. Auth Adoption Plan Concrete rollout order and milestones for zip, lobpress, inbox, DesBio, and DBScript. Host App Integration How Astro and Workers apps wire middleware, hooks, and routes. Provider Adapters The AuthAdapter contract and migration path across login providers. Data Model D1/MySQL schemas for identities, sessions, tokens, and rate limits.

Provider Adapters

Overview

A provider adapter implements the AuthAdapter interface from auth-core. It handles the mechanics of how a user proves their identity — the host app never sees these details.

The AuthAdapter interface

type AuthAdapter = {
  requestLogin(input: {
    email: string;
    redirectTo?: string;
    request: Request;
  }): Promise<{ redirectTo?: string }>;

  completeLogin(input: {
    token: string;
    request: Request;
  }): Promise<{ identity: AuthIdentity; session: unknown }>;

  resolveSession(request: Request): Promise<AuthIdentity | null>;

  logout(request: Request): Promise<void>;
};

The AuthIdentity type

Every provider returns the same identity shape:

type AuthIdentity = {
  provider: 'magic-link' | 'firebase' | 'better-auth';
  providerUserId: string;
  email: string;
  emailVerified: boolean;
};

This is stored in the auth_identities table, decoupled from the host app's product tables. A user can have multiple identities (one per provider) all mapping to the same host user ID.

Built-in providers

Magic-link (v1)

The default provider. No external dependencies.

How it works:

  1. requestLogin → normalizes email, generates token, hashes it, stores via StorageAdapter, sends email
  2. completeLogin → looks up hashed token, consumes atomically, creates session, returns identity
  3. resolveSession → reads the x-jampress-session-id header set by the framework adapter, loads session from storage, returns identity
  4. logout → reads the same header and deletes the session from storage

Config:

createMagicLinkProvider({
  storage,
  email,
  tokenTtlSeconds: 900,        // 15 minutes (default)
  sessionTtlSeconds: 604800,   // 7 days (default)
  rateLimits: {
    requestLogin: {
      email: { threshold: 5, windowSeconds: 900 },
      ip: { threshold: 20, windowSeconds: 900 },
    },
    completeLogin: {
      ip: { threshold: 10, windowSeconds: 900 },
    },
  },
  buildMagicLinkUrl(token, redirectTo) {
    const url = new URL(redirectTo || '/api/auth/verify', 'https://myapp.com');
    url.searchParams.set('token', token);
    return url.toString();
  },
})

Firebase Auth (v2, future)

Recommended for WordPress/hybrid projects and projects needing managed infrastructure.

Planned approach:

  • Firebase Admin SDK verifies tokens server-side
  • On login: Firebase handles the UI/email, issues a Firebase ID token
  • completeLogin verifies the Firebase token, extracts identity, creates SDK session
  • resolveSession checks SDK session cookie (not Firebase session cookie)
  • Firebase session cookies (2-week server-side) as an alternative mode

Why this adapter exists: Firebase handles OAuth providers, MFA, SAML, and compliance out of the box. When a project outgrows magic links, Firebase is the path of least resistance for projects that already have Firebase infrastructure (like DesBio with its Firebase JWT auth).

Better Auth (v2, future)

Recommended for Cloudflare-native projects that need more than magic links but want to stay self-hosted.

Planned approach:

  • Better Auth runs as a Worker or alongside the Astro app
  • Database-backed sessions using the same D1 instance
  • Built-in rate limiting, session management, schema tooling
  • completeLogin creates both a Better Auth session and an SDK session
  • resolveSession delegates to Better Auth's session resolution

Why this adapter exists: Better Auth is TypeScript-native, self-hosted, and database-backed. For Cloudflare Workers projects, it's a more natural evolution from DIY magic links than Firebase — same runtime, same database, just more features.

Writing a new adapter

To add support for another auth provider:

  1. Create packages/auth-provider-{name}/
  2. Implement the AuthAdapter interface
  3. Return AuthIdentity from completeLogin with provider: '{name}'
  4. Store sessions via the SDK's StorageAdapter (don't bypass it)
  5. Add a README with configuration instructions
  6. Add integration tests using the same test harness as magic-link

The key contract: your adapter MUST return a standard AuthIdentity. Everything after that — the resolveOrCreateViewer callback, the middleware, the cookie — is handled by the SDK.

Provider migration

See docs/ROADMAP.md v2 section for the dual-session migration approach.

The short version: both old and new provider sessions are valid simultaneously during a migration window. New logins use the new provider. Old sessions expire naturally. The auth_identities table maps both provider user IDs to the same host user.