Skip to content

About

Template examples for building seamless auth projects

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Seamless Templates

License: Apache-2.0

The frontend and API starter templates for Seamless Auth, an open source, passwordless authentication system.

This repository is the single source of truth for the starters that seamless-cli scaffolds. The CLI reads registry.json, presents the available templates during seamless init, and copies the chosen ones into a new project already wired to the auth server.

Start here

New to Seamless Auth? The self-hosted quickstart runs the full stack locally with Docker. If Seamless hosts your auth instance, follow the managed quickstart instead.

This repo holds the starters the CLI scaffolds, so it is not one node in the diagram below: the web, mobile, and API templates become the browser app, the native app, and your backend (the Next.js starter is both browser app and backend).

flowchart LR
  browser["Browser<br/>@seamless-auth/react"] -- "signed httpOnly cookies" --> backend
  native["Native app<br/>@seamless-auth/react-native"] -- "bearer tokens" --> backend
  backend["Your backend<br/>@seamless-auth/express, fastify, or nextjs<br/>mounted at /auth"] -- "bearer token + service token" --> api
  api["seamless-auth-api<br/>owns the session"] --> db[("Postgres")]
  backend -. "verifies tokens with JWKS" .-> api
Loading

How the pieces connect explains each hop. Compatibility matrix lists which package versions work together.

You usually do not clone this repository directly. Run the CLI instead:

npx seamless-cli init my-app

Repository layout

seamless-templates/
├─ registry.json              # the catalog the CLI reads to build its prompts
├─ templates/
│  ├─ web/
│  │  └─ <framework>/         # one directory per web starter
│  │     ├─ template.json     # how the CLI fetches and configures this template
│  │     ├─ .env.example      # the template's environment contract
│  │     └─ ...               # the actual starter project
│  ├─ api/
│  │  └─ <framework>/         # one directory per API starter
│  ├─ mobile/
│  │  └─ expo/                # the Expo (React Native) starter
│  └─ fullstack/
│     └─ nextjs/              # the Next.js starter, which serves /auth itself
├─ shared/
│  └─ react-app/              # source of truth for what both React starters share
└─ scripts/
   ├─ validate-templates.mjs
   └─ sync-shared.mjs

Each template is a complete, runnable project. The CLI downloads this repository at a pinned tag, copies the selected template directories into the new project (web/, api/, and optionally mobile/), and fills their .env files from each template's declared contract.

Because the CLI copies one template directory and nothing else, a template cannot reference anything outside itself. Anything two templates share therefore lives in shared/, and each template carries a committed copy of it. See shared/react-app/README.md.


The registry

registry.json is the catalog. Every template the CLI can offer has one entry:

{
  "schemaVersion": 1,
  "templates": [
    {
      "id": "react-vite", // unique, kebab-case
      "kind": "web", // "web", "api", "mobile", or "fullstack"
      "framework": "react",
      "label": "React (Vite)", // shown in the CLI prompt
      "alias": "basic", // optional: enables `seamless init --basic`
      "status": "stable", // "stable" | "beta" | "coming-soon"
      "path": "templates/web/react-vite",
    },
  ],
}

status: "coming-soon" advertises a template in the CLI as a disabled option without requiring its content to exist yet. alias (optional) lets users select the template directly with seamless init --<alias>, skipping the prompt.

The template manifest

Each template directory carries a template.json that tells the CLI where to place it and how to configure its environment. This is what replaces per-framework wiring living inside the CLI:

{
  "id": "react-oauth",
  "targetDir": "web",
  "env": {
    "fromExample": ".env.example",
    "set": {
      "VITE_AUTH_SERVER_URL": "{{authServerUrl}}",
      "VITE_API_URL": "{{apiUrl}}",
    },
  },
  "verify": {
    // optional: how `seamless verify` tests this template
    "project": "react", // the Playwright project that drives it
    "flows": ["oauth"], // which flow tags to run (@oauth); omit to run all
  },
  "setup": {
    // optional: interactive setup the CLI runs
    "oauth": true, // prompt for OIDC providers and wire them into auth
  },
  "requires": { "cliMin": "0.3.0" },
}

The CLI computes the shared values and resolves the {{...}} placeholders in env.set. A new framework with different variable names (for example NEXT_PUBLIC_*) only needs a different set map, not a CLI change.

verify.flows scopes conformance to the flows a template actually supports (the harness tags specs @login, @oauth, ...); a template with no verify block runs the full browser suite. setup.oauth tells the CLI to prompt for OIDC providers (Google, GitHub, Microsoft, GitLab) and wire the chosen ones into the scaffolded auth server. Both fields are ignored by older CLIs, so they degrade gracefully.

Placeholder vocabulary

Placeholder Resolves to
{{authServerUrl}} URL of the Seamless Auth server
{{apiUrl}} URL of the project's API service
{{apiToken}} Service token shared between the API and the auth server
{{jwksKid}} JWKS key id the auth server signs with
{{serveAdminConsole}} Whether the API serves the admin console at /console
{{databaseUrl}} Connection string for the project's database
{{secret:N}} A freshly generated N-byte hex secret, unique per scaffold

Adding a template

  1. Create templates/<kind>/<framework>/ with a complete, runnable starter.
  2. Add a committed .env.example describing its environment contract.
  3. Add a template.json manifest (see above).
  4. Add an entry to registry.json.
  5. If it is a React web starter, add it to targets in shared/react-app/sync.json and run npm run sync:shared.
  6. Run npm run validate and open a pull request.

CI validates the registry and every manifest, then installs each template and runs its typecheck, lint, format check, tests, and build to confirm it works before it ships. The mobile template's build is expo export, which bundles the JavaScript for iOS and Android without Xcode or the Android SDK; it proves the bundle, not a signed native binary.

Checks every template ships

A scaffolded project is expected to be verifiable on the first npm install, so each template declares the same script names. CI runs them with --if-present, and a user gets the whole set locally with npm run check.

Script Purpose
typecheck TypeScript with no emit
lint ESLint flat config over the project
format:check Prettier, with eslint-config-prettier keeping the two from disagreeing
test Vitest, no database or network needed
check All of the above in one command

Tests sit next to the code they cover as *.test.ts / *.test.tsx. They are meant to be a starting point a user extends, not exhaustive coverage: they cover the configuration and startup logic that decides whether a fresh scaffold runs at all. The mobile template tests its pure modules this way; its screens are React Native and are exercised on a simulator rather than in Vitest.

Mobile templates

A kind: "mobile" template is placed at mobile/ next to web/ and api/, and is optional in seamless init. It differs from the web starters in one way that is onboarding cost rather than code: native passkeys need the relying party to be a domain the adopter controls, with apple-app-site-association and assetlinks.json hosted over HTTPS, because native WebAuthn has no localhost exemption. Email codes and magic links work against the local stack immediately; passkeys do not until that domain exists. The Expo starter's README and tools/associations/ cover the setup.

Full-stack templates

A kind: "fullstack" template is one project that is both the web application and its backend. The Next.js starter serves the Seamless Auth /auth routes itself through @seamless-auth/nextjs, reads the session in server components, and protects pages in proxy.ts, so it needs no api template beside it. It is placed at web/ and serves on port 5173, like the web starters.

seamless-cli 0.18.0 and later offer it as a beta template: seamless init my-app --nextjs (or --web=nextjs) scaffolds it into web/, and seamless check treats the web app as the backend. Its manifest requires that version (requires.cliMin), and older CLIs never show the kind. A full-stack template cannot be combined with an api template, and it cannot host the admin console yet, so only --admin=none applies. seamless verify in CLI 0.18.0 skips full-stack templates (fells-code/seamless-cli#222); coverage for the Next.js starter is on seamless-cli main but not in a release yet.

It takes the design tokens and fonts from shared/react-app but not the UI kit, which routes with react-router. shared/react-app/sync.json lists it as a target with an only list for that reason.


Local development

npm install
npm run validate

npm run validate checks that registry.json is well-formed, that every referenced template has a valid template.json and .env.example, and that no template copy of a shared source has drifted from shared/.

To change something both React starters use (the design tokens, the app shell layout, the UI kit, or the fetch seam under lib/), edit the file under shared/react-app/ and run:

npm run sync:shared

Versioning

Releases are managed with Changesets and published as git tags. The CLI pins a specific tag, so scaffolding is reproducible. Add a changeset with any change that affects scaffolded projects:

npm run changeset

License

Apache-2.0 © 2026 Fells Code LLC

See LICENSE for the full text.

About

Template examples for building seamless auth projects

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages