Skip to content

Latest commit

 

History

History
137 lines (125 loc) · 8.41 KB

AGENTS.md

File metadata and controls

137 lines (125 loc) · 8.41 KB

This is Convex Auth, an authentication framework built on top of Convex. We’re currently bulding the v2 version of the system, where each sign-in method uses one or more Convex components.

Repository layout

This is a pnpm monorepo:

  • packages/core — the auth package, published as @convex-dev/auth. It owns three parts:
    • The core: sessions, accounts, and JWT minting. It knows no sign-in method.
    • The optional components, which store the data of the sign-in methods: username, password, email, passkey, totp, anonymous, oauth, google, apple, and github. Most of them key their data by an opaque user id. totp is a second factor, not part of a sign-in method, and the package does not export it for now. The package does not export anonymous and the generic oauth component for now either.
    • The sign-in methods, which the app wires up: anonymous (not exported for now), username-password, email-password, username-passkey, oauth (only its shared React client is exported for now; the API for custom providers is not), google, apple, and github.
  • packages/password-hash-wasm — the password-hash-wasm package wrapping the argon2id WASM hasher used by the password component. It publishes compiled JavaScript plus declarations from dist/, so consumers never typecheck its source. Its prepack script builds the Rust/WASM output and then compiles the TypeScript wrapper, which keeps the Rust toolchain a publish-time requirement — pnpm install and TypeScript-only work don't need it. packages/core depends on the published package, not the workspace one, for the same reason.
  • packages/doc-examples — a private Convex app that installs every component and sets up every sign-in method. The code examples in the doc comments of packages/core/src compile against it (pnpm check:doc-examples). It is not an example to copy.
  • packages/docs — the documentation site (private). The pages are in docs/.
  • packages/passkey-test-authenticator — a software WebAuthn authenticator for the passkey tests of this repository (private).
  • examples/ — example apps, each an in-repo Convex backend run from its own directory:
    • react-password wires up the core, password, and username components with the usernamePassword method.
    • react-email-password wires up the core, password, and email components with the emailPassword method (sign-up with email validation, change password/email, password recovery; email through @convex-dev/resend).
    • react-passkey wires up the core, passkey, and username components with the usernamePasskey method, and adds passkey management.
    • react-passkey-basic is react-passkey without passkey management. The passkey tutorial uses it as its first step.
    • react-google wires up the core and the google component with the google method.
    • react-apple wires up the core and the apple component with the apple method.
    • react-github wires up the core and the github component with the github method.
    • nextjs is a Next.js (App Router) app with SSR sign-in. It wires up the usernamePassword and github methods.

Source layout of packages/core/src

  • component/ — the core Convex component (auth): sessions, accounts, JWTs.
  • components/<name>/ — the optional Convex components (username, password, email, passkey, totp, anonymous, oauth, google, apple, github).
  • methods/<method>/ — the sign-in methods, which use one or more components: server.ts (Convex functions of the app), react.tsx or react.ts (hooks), and the browser code. A method is published at @convex-dev/auth/methods/<method>/server and @convex-dev/auth/methods/<method>/react. A method whose backend and clients share values (such as DEFAULT_MIN_PASSWORD_LENGTH) also has an index.ts, published at @convex-dev/auth/methods/<method>, which any runtime can import.
  • server/ — the app-side code of the core (setupConvexAuth, getAuthUserId).
  • ssr/, react/, browser/ — the client and SSR code of the core. ssr/nextjs/ holds the Next.js bindings.
  • lib/ — internal code that several layers share (lib/oauth/ for the OAuth components and methods).
  • testing/ — the convex-test helpers, which ship as source.

The Convex CLI deploys every module in a component directory, and codegen lists each one in _generated/api.ts. Thus a component directory holds only the code of the component. Put the other code in the directory of its layer, as the list above shows: the code of a sign-in method in methods/<method>/, the app-side code of the core in server/, the client and SSR code of the core in ssr/, react/, or browser/, and shared code in lib/. ESLint rejects a component module that imports client code or app-side code. The CLI skips a file name with two dots (x.test.ts, x.fixture.ts): use the .fixture.ts suffix for a test-only module in a component directory.

Published output

@convex-dev/auth ships compiled ESM plus declarations under dist/; its exports map points there, never at src/ (password-hash-wasm does the same). This matters: a package whose exports resolve to .ts gets typechecked by the consumer's tsconfig.json, so our source would have to satisfy their lib, types, and verbatimModuleSyntax settings. Two rules follow from it:

  • Relative imports in packages/core/src carry the extension of the file on disk (./cookies.ts, ./client.tsx). packages/core/tsconfig.json sets moduleResolution: NodeNext, so an import without an extension is a compile error (TS2835) in the editor and in tsc --noEmit, and rewriteRelativeImportExtensions turns each one into .js in the emitted JavaScript, which is what Node's ESM resolver needs. Declarations keep the .ts specifier; a consumer's TypeScript reads that as the sibling .d.ts, back to TypeScript 5.0. Files under _generated/ keep the .js extensions that convex codegen writes, and both spellings compile. The examples and docs are free to stay extensionless.
  • Anything reached from a published entry point must compile. dist/ is built by tsconfig.build.json, which excludes tests and other in-repo-only modules (see its exclude list).
  • Each exports entry names one file. Do not use a *: a wildcard publishes every file of a directory, including the internal modules and the _generated/ files of a component. pnpm check:declarations rejects a wildcard and an entry whose file does not exist.

The one exception is the ./test and ./components/<name>/test entries, which ship as TypeScript from src/. Those helpers call import.meta.glob, a Vite macro that has to be transformed by the consumer's bundler — and Vitest externalizes plain .js under node_modules (leaving the macro intact, so it throws at import) while it cannot externalize .ts. Shipping source is what makes them work. They carry their own /// <reference types="vite/client" /> so a consumer doesn't need vite/client in their tsconfig. Their schema import uses the extension of the file on disk, the same as the rest of src/. The consumer's tsc reads these files, thus the consumer must set allowImportingTsExtensions; without it, the .ts extension is an error (TS5097). Each example app that imports a test helper sets it in its convex/tsconfig.json, and pnpm typecheck catches a regression here.

Use pnpm install at the root. Common tasks:

  • pnpm build — compile packages/core to dist/. Runs automatically via prepare on install; run it by hand after changing its sources if you want the examples to pick the change up. (password-hash-wasm is consumed from npm, so it isn't built here — see its prepack.)
  • pnpm codegen — regenerate every Convex _generated directory. Can also generate the codegen for a single demo or component (e.g. pnpm codegen react-passkey)
  • pnpm test — run tests across packages (Vitest).
  • pnpm typecheck — typecheck across packages.
  • pnpm check:doc-examples — typecheck the ts and tsx code blocks in the doc comments of packages/core/src as modules of packages/doc-examples (run pnpm build first). When an example uses a function or a component that the app does not have, add it to the app. See packages/doc-examples/README.md.
  • pnpm lint — ESLint, including the Convex ESLint plugin.
  • pnpm format — Prettier.