Caddy module to require at-proto authentication and restrict routes to DIDs
2

Configure Feed

Select the types of activity you want to include in your feed.

7 1 0

Clone this repository

https://tangled.org/vvill.dev/caddy-atproto-auth https://tangled.org/did:plc:fodfgwa72st5kdv5tbcvxfmg
git@knot.vvill.dev:vvill.dev/caddy-atproto-auth git@knot.vvill.dev:did:plc:fodfgwa72st5kdv5tbcvxfmg

For self-hosted knots, clone URLs may differ based on your setup.



README.md

caddy-atproto-auth#

A native Caddy module that provides Identity-Aware Proxy (IAP) capabilities using the atproto (Bluesky) OAuth 2.1 ecosystem.

Turn any atproto identity into a "Web Passport" for your self-hosted services. The module acts as an OAuth Confidential Client, managing the DPoP cryptographic handshake, session persistence, and DID-based authorization without requiring external authentication sidecars like Authelia.

Features#

  • Zero-Dependency: Plugs directly into Caddy, no external databases (uses embedded SQLite).
  • Stateless Verification: Uses signed, domain-scoped cookies for lightning-fast request verification at the edge without database lookups.
  • Transparent Session Refresh: Automatically uses OAuth Refresh Tokens to extend sessions in the background, minimizing forced re-logins.
  • Two Deployment Modes:
    • Standalone: Add to any individual app's Caddyfile route directly.
    • Centralized Hub: Act as an Identity Provider (auth.example.com) granting SSO access to many subdomains (app.example.com).
  • Full Customization: Fully override the login and forbidden pages with your own HTML templates.

Usage#

Build a custom Caddy binary with xcaddy:

xcaddy build \
    --with tangled.org/vvill.dev/caddy-atproto-auth

Configuration#

Global Options#

The atproto global block configures the shared storage and security settings.

{
    atproto {
        # Path to the SQLite database.
        # Default: "atproto.db"
        storage_path /var/lib/caddy/atproto.db

        # A random 32+ character string used to sign session cookies.
        # REQUIRED.
        cookie_secret "change-me-to-a-secure-random-string-at-least-32-chars"
    }
}

Authentication Portal (atproto_portal)#

The atproto_portal directive configures the central authentication server. This handles the OAuth flow, serves the login page, and issues session cookies.

auth.example.com {
    atproto_portal {
        # The public domain of the portal.
        # REQUIRED.
        domain auth.example.com

        # The display name shown on the login page.
        # Default: "Authentication Portal"
        name "My Services"

        # Custom UI templates (optional)
        ui {
            # Path to a custom HTML template for the login page.
            login_template /path/to/login.html
        }
    }
}

Authentication Gate (atproto_gate)#

The atproto_gate directive protects your services. It verifies the session cookie and enforces access control.

app.example.com {
    atproto_gate {
        # List of allowed identities (DIDs or Handles).
        # REQUIRED.
        allow @alice.bsky.social
        allow did:plc:1234abcd...

        # URL of the central Auth Portal. 
        # Requests without a valid session will be redirected here.
        # REQUIRED (unless in Standalone Mode).
        portal_url https://auth.example.com

        # Standalone Mode Configuration (Alternative to portal_url)
        # If set, this gate acts as its own portal.
        # domain app.example.com

        # Custom UI templates (optional)
        ui {
            # Path to a custom HTML template for the "Access Denied" page.
            forbidden_template /path/to/forbidden.html
        }
    }

    reverse_proxy localhost:8080
}

Documentation#

See the docs/ folder for detailed architectural constraints and implementation details.