UNPKG

@lunora/cli

Version:

The Lunora CLI: init, dev, deploy, codegen, migrate, seed, doctor, insights, logs, registry, and the rest of the project commands

171 lines (131 loc) 6.49 kB
--- name: lunora-setup-auth description: Adds authentication to a Lunora app. Use for sign-up/sign-in, email/password, OAuth (Clerk, Auth0), magic link, or email OTP via `lunora registry add auth`, wiring the auth handler into the Worker, and gating functions on the session. --- # Lunora Setup Auth Wire authentication into a Lunora app using the `auth` registry item, which is built on `@lunora/auth` (a thin wrapper over [better-auth](https://www.better-auth.com)) with sessions persisted in `SessionDO` and identity tables in D1. ## When to Use - Adding sign-up / sign-in to a Lunora app. - Adding an OAuth/OIDC provider (Clerk, Auth0), magic link, or email OTP. - Gating queries/mutations on the signed-in user. ## When Not to Use - The project has no Lunora backend yet — use `lunora-quickstart` first. - You only need to read `ctx.auth.userId` in a function and auth is already installed — just use it. ## Workflow 1. Add the base `auth` item. 2. Mount the auth request handler in the Worker entry. 3. Configure env vars and the D1 database. 4. (Optional) Layer a provider item (Clerk / Auth0 / magic link / OTP) on top. 5. Gate functions on `ctx.auth.userId`; gate UI with the auth gates/hooks. ## Step 1: Add the base item ```bash lunora registry add auth ``` This: 1. Adds `@lunora/auth`, `@lunora/mail`, and `@lunora/server` to `package.json` (run `pnpm install` afterwards). 2. Copies `lunora/auth/index.ts` — the auth instance (`buildAuth` / `getAuth`) and the `/api/auth/*` request handler (`mountAuth`) — into your project. It is **yours** to edit. 3. Adds a D1 `DB` binding to `wrangler.jsonc` (better-auth persists users/sessions there). 4. Scaffolds `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL`, and `MAIL_FROM` into `.dev.vars`. ## Step 2: Mount the handler In your Worker entry, route `/api/auth/*` to the scaffolded handler (see the generated `lunora/auth/index.ts` README block). `createWorker` handles the rest of the RPC surface; the auth handler owns the better-auth endpoints. ## Step 3: Env vars and the D1 database | Var | Secret | Notes | | -------------------- | ------ | ------------------------------------------------------------------- | | `BETTER_AUTH_SECRET` | yes | Encryption secret, min 32 chars. `openssl rand -base64 32`. | | `BETTER_AUTH_URL` | no | Public base URL, e.g. `http://localhost:8787` in dev, your domain. | | `MAIL_FROM` | no | Sender for verification / reset mail. Captured in the dev Mail tab. | Create the D1 database and paste its id into the `DB` binding in `wrangler.jsonc`: ```bash wrangler d1 create my-app-db ``` The better-auth schema (user/session/account/verification tables) is **not** declared in `lunora/schema.ts` — it is managed by better-auth in D1. In dev, `ensureMigrated(auth)` auto-applies it; in production prefer `compileMigrationsSql(auth.options)` piped to `wrangler d1 execute`. Run `lunora doctor` to confirm the `DB` binding has a real `database_id` (not a placeholder). Verification and password-reset emails are **captured into the Lunora Studio Mail tab** in dev with zero email setup. For real delivery, `lunora registry add mail` (adds the `SEND_EMAIL` binding) or set `RESEND_API_KEY`. ## Step 4: Add a provider (optional) Each provider item builds on the base `auth` item (`requires: ["auth"]`): ```bash lunora registry add auth-clerk # Clerk via better-auth genericOAuth lunora registry add auth-auth0 # Auth0 via better-auth genericOAuth lunora registry add auth-magic-link # passwordless magic-link (mail) lunora registry add auth-otp # passwordless email one-time-password ``` Add the base `auth` item first (or let the registry resolve the `requires` dependency). OAuth items need the provider's client id/secret added to `.dev.vars`. ## Step 5: Use the session ### In functions The runtime resolves the session and exposes the user on every context: ```ts import { LunoraError, mutation, v } from "@lunora/server"; export const createDocument = mutation.input({ title: v.string() }).mutation(async ({ ctx, args: { title } }) => { if (!ctx.auth.userId) { throw new LunoraError("UNAUTHORIZED", "not signed in"); } return ctx.db.insert("documents", { ownerId: ctx.auth.userId, title, createdAt: Date.now() }); }); ``` For richer checks (org membership, roles), compose `withAuthPlugins(auth)` and call the better-auth server API — see the scaffolded `lunora/auth/index.ts`. ### In the UI (React) ```tsx import { Authenticated, Unauthenticated, useAuth } from "@lunora/react"; function Account() { const { user, signIn, signOut } = useAuth(); return ( <> <Authenticated> <span>Signed in as {user?.email}</span> <button type="button" onClick={() => signOut()}> Sign out </button> </Authenticated> <Unauthenticated> <button type="button" onClick={() => signIn()}> Sign in </button> </Unauthenticated> </> ); } ``` `@lunora/react` also exports `AuthLoading` and `useAuthState` for the loading window before the session resolves. ## Common Pitfalls 1. **Declaring better-auth tables in `lunora/schema.ts`.** They live in D1 and are managed by better-auth — do not add them to `defineSchema`. 2. **Placeholder `database_id`.** The `DB` binding ships with a placeholder; `wrangler d1 create` + paste the id, then `lunora doctor` to confirm. 3. **Missing/short `BETTER_AUTH_SECRET`.** better-auth needs ≥32 chars; `@lunora/auth` surfaces a clear error when it is absent. 4. **Expecting prod email to "just work".** Dev captures mail into the Studio; production needs `mail` (the `SEND_EMAIL` binding) or `RESEND_API_KEY` and a verified sender domain. ## Checklist - [ ] `lunora registry add auth` run, `pnpm install` done. - [ ] Auth handler mounted in the Worker entry. - [ ] `BETTER_AUTH_SECRET` / `BETTER_AUTH_URL` / `MAIL_FROM` set in `.dev.vars`. - [ ] D1 database created and its id pasted into the `DB` binding (`lunora doctor` clean). - [ ] Provider item added if needed (Clerk / Auth0 / magic link / OTP). - [ ] Functions gate on `ctx.auth.userId`; UI uses the auth gates/`useAuth`. - [ ] Verified sign-in → session → an authenticated query round-trip.