Clerk Authentication

Clerk authentication: production vs test keys, custom domain DNS, user migration between instances, and GitHub OAuth setup

Overview

Verifieddit uses Clerk for authentication. Clerk handles user sign-up/sign-in, session management, and JWT verification. There are separate production and testing instances.

Instances

Production Instance

  • Frontend key: pk_live_* (baked into Vite build via VITE_CLERK_PUBLISHABLE_KEY)
  • Custom domain: clerk.verifieddit.com
  • Dashboard: https://dashboard.clerk.com (select production instance)

Testing Instance

  • Frontend key: pk_test_* (baked into testing build)
  • Domain: Default Clerk domain (no custom domain for testing)
  • Dashboard: https://dashboard.clerk.com (select testing instance)

Key Management

Production vs Test Keys

Key TypeProductionTesting
Publishable Keypk_live_*pk_test_*
Secret Keypass verifieddit/clerk/secret-keypass verifieddit/clerk/test-secret-key

Critical: The publishable key is baked into the Vite build at compile time. Changing the key requires:

  1. Update VITE_CLERK_PUBLISHABLE_KEY in flake.nix
  2. Recompute the FOD hash (since the build output changes)
  3. Rebuild and redeploy

Where Keys Are Used

ComponentKey SourceKey Type
Vite app (frontend)flake.nix build varPublishable key
Badges WorkerWorker secretSecret key
Stripe BackendPulumi secretSecret key
Testing WorkerWorker secret (test)Test secret key

Custom Domain Setup

DNS Records Required (5 total)

See Cloudflare DNS SOP for the full DNS setup procedure.

  1. clerk.verifieddit.com CNAME -> frontend-api.clerk.services
  2. accounts.verifieddit.com CNAME -> accounts.clerk.services
  3. clk._domainkey.verifieddit.com CNAME -> dkim1.<instance-id>.clerk.services
  4. clk2._domainkey.verifieddit.com CNAME -> dkim2.<instance-id>.clerk.services
  5. clkmail.verifieddit.com CNAME -> mail1.<instance-id>.clerk.services

Critical: Proxy Setting

All Clerk DNS records MUST have proxied: false in Cloudflare. Clerk needs direct access to provision SSL certificates. If records are proxied:

  • SSL certificate provisioning fails
  • Clerk shows “DNS verification pending”
  • Users cannot sign in via the custom domain

SSL Certificate Provisioning

After DNS records are created:

  1. Clerk begins verification (automatic)
  2. SSL certificate provisioning takes up to 24 hours
  3. Check status in Clerk Dashboard > Domains
  4. Once verified, Clerk serves auth pages from the custom domain

GitHub OAuth Setup

To enable “Sign in with GitHub” on Clerk:

  1. Create GitHub OAuth App:

    • Go to https://github.com/settings/developers
    • Create new OAuth App
    • Homepage URL: https://verifieddit.com
    • Authorization callback URL: https://clerk.verifieddit.com/v1/oauth_callback (or the Clerk-provided callback URL)
  2. Add to Clerk:

    • Clerk Dashboard > User & Authentication > Social connections
    • Enable GitHub
    • Enter Client ID and Client Secret from the GitHub OAuth App
  3. Verify: Sign in flow should show “Continue with GitHub” option

User Migration Between Instances

When migrating users from one Clerk instance to another (e.g., testing to production):

Export Users

1
2
3
4
5
CLERK_SECRET=$(pass verifieddit/clerk/secret-key)

# List all users
curl -s -H "Authorization: Bearer ${CLERK_SECRET}" \
  "https://api.clerk.com/v1/users?limit=100" | jq '.data[] | {id, email: .email_addresses[0].email_address}'

Create Users in Target Instance

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
TARGET_CLERK_SECRET=$(pass verifieddit/clerk/production-secret-key)

# Create user
curl -s -X POST \
  -H "Authorization: Bearer ${TARGET_CLERK_SECRET}" \
  -H "Content-Type: application/json" \
  "https://api.clerk.com/v1/users" \
  --data '{
    "email_address": ["user@example.com"],
    "skip_password_requirement": true
  }'

D1 Data Migration

After migrating users in Clerk, update the D1 database:

1
2
3
4
5
6
# Export users from testing D1
npx wrangler d1 execute verifieddit-badges-testing \
  --command "SELECT * FROM users" --json > users-export.json

# Import into production D1
# (manually construct INSERT statements from the JSON export)

CSP Configuration

The Content Security Policy must include Clerk domains:

connect-src 'self' https://*.clerk.com https://clerk-telemetry.com;
script-src 'self' https://*.clerk.com;
frame-src 'self' https://*.clerk.com;

Known issue: clerk-telemetry.com must be explicitly listed in CSP or telemetry requests will be blocked, causing console errors.

Troubleshooting

  • “Guest mode only”: The Clerk publishable key is missing from the build. Check VITE_CLERK_PUBLISHABLE_KEY in flake.nix. This is preflight check #13.
  • SSL certificate not provisioning: Ensure all 5 DNS records have proxied: false. Check with dig clerk.verifieddit.com – it should resolve directly to Clerk, not Cloudflare.
  • CSP errors in console: Add clerk-telemetry.com to CSP connect-src.
  • User not found after migration: Verify the Clerk user ID matches between the old and new instances. D1 references are by clerk_id.
  • OAuth callback error: Verify the callback URL in the GitHub OAuth App matches exactly what Clerk expects.