SvelteKit Auth exampleCloudflare

SvelteKit Auth

Auth.js sign-in for SvelteKit, with no database.

sveltekit-auth

Auth.js's SvelteKit example — a home page, a sign-in page, a page only a signed-in visitor can read and a sign-out, over @auth/sveltekit with the example's 26 OAuth providers — running on Cloudflare Workers + Static Assets through SvelteKit's own @sveltejs/adapter-cloudflare, and packaged as an Orun baseline: one command takes a new site from nothing to live on stage and prod, in its own repository, under its own name, in ONE landing.

Sessions are the example's: Auth.js's encrypted JWT cookie, so there is no database. Each provider is offered once its keys are stored as the site's secrets, so the site goes live with none connected and its sign-in page says what to connect; the AUTH_SECRET that seals the sessions is generated on each environment's Worker by its first deploy, so nothing is typed. What the baseline adds is everything needed to make a copy of it somebody else's, built the way cirrus-baseline is. The design, the milestones and the as-built status are in specs/epics/sveltekit-auth. ISC, © 2022-2024 Balázs Orbán (apps/web/LICENSE).

Instantiating a site

git clone --depth 1 --branch <baseline-tag> https://github.com/sourceplane/sveltekit-auth
cd sveltekit-auth
orun new --blueprint repo-blueprint.yaml --out ~/sites/acme-accounts --run-hooks --resume \
  --set reponame=acme-accounts --set productname="Acme Accounts" \
  --set githuborg=acme-inc --set subdomain=<workers-dev-subdomain>

Or, once the platform's registry lists it, from the Orun console (Baselines → sveltekit-auth → Build). Two phases: 01-product creates the repository and lands the whole site as ONE pull request — intent, CI, tooling, the site and its tests — and one convergence builds it, gives each environment's Worker its AUTH_SECRET and deploys it to stage and prod; 03-docs records the live deployment from probed facts. BOOTSTRAP.md is the operator guide; docs/phases/ has a page per phase.

The new repository receives the site only — the app and its tests, its CI, its tooling, a README of its own and its operating contract — renamed throughout to the site's name, repository and Cloudflare Worker, with Auth.js's own names (SvelteKitAuth, sveltekit-auth-example) and the example's licence left as they are. None of this repository's machinery ships.

What makes it a baselineWhere
The console card (inputs, secrets, programme)blueprint.yaml
The build document (modules, phases, hooks)repo-blueprint.yaml
This tree's identity, and the bootstrap's task contracts.baseline/
The rename enginetooling/rebrand/rebrand.mjs
The bootstrap's helpers (deferrals, redeploy marker, work-tree strip)tooling/bootstrap/
The deployment-record rendererhooks/render-deployment-docs.sh
The site's README and context pack.baseline/product/README.md, ai/context/
The baseline's tests: contract, leak, offline bootstrap, end to endtesting/, .github/workflows/baseline.yml
Cutting a baseline-vN tag.github/workflows/tag.yml

What is here (the site)

SurfaceWhere
The site's name (the home page's heading, every page's <title>)apps/web/src/site.ts
/, /signin (the connected providers, or what to connect), /protected, /signoutapps/web/src/routes
Auth.js, configured per request from the Worker's secretsapps/web/src/auth.ts, apps/web/src/hooks.server.ts, apps/web/src/server/env.ts
The 26 providers and the keys each needsapps/web/src/server/providers.ts
The header (who is signed in) and the footerapps/web/src/components
The deploy step that gives each Worker its secretstooling/wire/worker-secrets.mjs
Adapter, Worker config, deploy contractapps/web/svelte.config.js, apps/web/wrangler.jsonc, apps/web/component.yaml
Tests (the providers, the bindings, the site's name, the secret step)apps/web/src/**/*.test.ts (vitest)

Getting started

pnpm install          # runs `svelte-kit sync`
pnpm typecheck        # svelte-check
pnpm lint             # prettier --check
pnpm test             # vitest
pnpm build            # vite build → apps/web/.svelte-kit/cloudflare (_worker.js + the static output)
pnpm --filter @app/web secret:dev   # a generated AUTH_SECRET in apps/web/.env (git-ignored)
pnpm --filter @app/web dev          # http://localhost:3000
pnpm --filter @app/web preview      # wrangler dev over the built Worker

Both read apps/web/.env; .env.example beside it, the example's own, lists every key the site takes. Add a provider's keys there (GitHub: an OAuth app whose callback is http://localhost:3000/auth/callback/github) and its button appears on /signin.

Deploying

The Orun pipeline (.github/workflows/ci.yml) plans nothing while intent.yaml names the placeholder workspace ws_CHANGE_ME. Link the repository to an Orun Cloud workspace (orun cloud link), set execution.state.workspace to it, connect Cloudflare in the workspace, and every merge to main then deploys sveltekit-auth-web-stage, then -prod: built, its secrets pushed to the Worker (wrangler secret bulk, the AUTH_SECRET generated on the first deploy and kept after), deployed and smoke-tested on / and /auth/session. A pull request verifies the same lanes and deploys nothing. A manual run of ci.yml (Run workflow on main, or Orunbase's Deploy button) redeploys the components it names — which is how a provider's newly stored keys reach the Worker.

Sign-in providers

None is connected at first. A provider appears once every key it takes is set: AUTH_GITHUB_ID + AUTH_GITHUB_SECRET for GitHub, and the same shape for the others (Auth0 adds AUTH_AUTH0_ISSUER). Deployed, they are optional Orun secrets per environment (orun secrets set AUTH_GITHUB_ID --env stage), pushed to the Worker by its next deploy; the OAuth app's callback URL is <origin>/auth/callback/<provider>. The component carries secret refs for the nine providers upstream's .env.example names; the site's README ("Sign-in providers") says how to deploy the others.

How it maps to Vercel

The example on VercelHere
@sveltejs/adapter-auto (→ adapter-vercel)@sveltejs/adapter-cloudflare: the Worker at .svelte-kit/cloudflare/_worker.js, the static output beside it (wrangler.jsonc)
"@auth/sveltekit": "latest"1.11.3, exact; SvelteKit, Svelte and Vite at upstream's versions, exact, and upstream's cookie@0 override
AUTH_SECRET typed into Vercel's environment variablesgenerated on each environment's Worker by its first deploy (tooling/wire/worker-secrets.mjs), kept by every later one; an Orun secret of the same name wins
provider keys in Vercel's environment variables, all 26 providers always registeredoptional Orun secrets pushed as Worker secrets; a provider is registered only when its keys are set, and /signin says what to connect
$env/dynamic/private from the platform's envthe Worker's bindings (platform.env), read per request; .env locally, as upstream, for vite dev and wrangler dev alike
sessions: Auth.js's JWT cookiethe same; no database
Deploy on pushOrun deploys <repo>-web-{stage,prod} on merge to main (apps/web/component.yaml), smoke-tested
"Deploy with Vercel" (clone the example)orun new --blueprint repo-blueprint.yaml — a new repository, branded, deployed

CI

WorkflowWhat it runsShips to a site
ci.ymlthe Orun pipeline, and the only workflow a site carries: orun plan --changed, then one job per lane. A pull request runs the verify lanes (the site built and dry-deployed per environment, no secret pushed; they do not typecheck, lint or run the tests), except the bootstrap's own landings (orun/BASE-*), where plan alone is the check; a merge deploys stage → prod. It plans nothing while intent.yaml names the placeholder workspace ws_CHANGE_ME, as this repository does. A manual run (Orunbase's Deploy button, or Run workflow on main) plans the environment and components it names under the push-to-main trigger, so they deploy as a merge deploys themyes
baseline.ymlthe baseline's own tiers: contract tests, the blueprint read by the pinned orun, the leak gate, the offline bootstrap with the site's own plan, the site installed, type-checked, linted, tested, built and dry-deployed in the lane's tree and served — with no secret (the smoke refuses it), with one (every page), and with GitHub's keys (the authorize URL, a session, sign-out) (testing/e2e.test.sh, before every tag)no
tag.ymlcuts a baseline-vN tag (manual)no