Paste your deployment name or its URL and every command on this page fills itself in. Both .convex.cloud and .convex.site work — auth is served from .convex.site, which is the one that’s easy to get wrong.

Way 1 — lazy

aussieauth.com mints the sessions. Your project keeps its own Convex deployment for its own data; that deployment verifies the token and nothing more. There is no auth code in your repo, no auth tables in your schema, no BETTER_AUTH_SECRET, and no provider credentials to collect.

Three commands, from a project that already has Convex:

bun add @aussieljk/auth
bunx aussieauth
bunx convex dev

That's it. Sign-in works at your dev server's /sign-in, with every method the hosted deployment has credentials for.

Nothing here needs a credential, a dashboard or a human. There is no secret to fetch, no origin to add by hand and no account to create. An agent can run these three commands start to finish and check its own work with bunx aussieauth doctor, which exits non-zero while anything is still wrong.

What each command did

bun add @aussieljk/auth — the card, the client and the aussieauth CLI.

bunx aussieauth — the whole install. (aussieauth init is the same thing with a name on it.) First it asks the deployment four questions — is it there, does it serve auth, does it publish a JWKS, is it current with this client — and refuses to write anything if any answer is wrong. A scaffold that completes against a deployment which cannot serve it is the worst outcome available: it reads as finished and fails later somewhere else.

Then it worked out the framework (Vite, Next, TanStack Start or Expo) and wrote four things:

WrittenWhy
src/auth/Providers.tsxThe AussieAuth client and the Convex client, wired together
a sign-in route<AussieAuthSignIn />, at the place your framework puts routes
convex/auth.config.tsVerifies AussieAuth's signature, so functions see an identity
.env.local…_AUSSIEAUTH_URL, pointing at the hosted deployment

It also registered your dev origin, so localhost is trusted before you've read a word about origins — no secret required. It works out the real origin from your dev script rather than guessing a port, including the https://<name>.localhost that portless serves on. If registering fails, the command fails with it; it never reports success over a half-finished setup.

Why development origins need no secret

A registration whose origins are all development ones — localhost, a .local name, a LAN address, on any port — is accepted without credentials, and any dev origin is trusted by the deployment the moment it asks, registered or not.

That is deliberate, and it is not a hole. Sending a request from http://localhost:5173 already means running code on that machine, so a shared secret in front of it protects nothing while costing the only thing that matters: an agent setting your project up cannot go and ask a human for a secret. Public origins are unchanged and still need AUSSIEAUTH_SECRET, and a secretless call is refused outright if the app slug it names already owns one.

bunx convex dev — pushed convex/auth.config.ts. Skipping this is the one way to end up somewhere confusing: sign-in succeeds in the browser and every query still sees null, because the deployment doesn't yet know who signs the tokens.

The only backend file

// convex/auth.config.ts
export default {
  providers: [
    {
      type: "customJwt",
      issuer: "https://giddy-dinosaur-765.convex.site",
      applicationID: "convex",
      algorithm: "RS256",
      jwks: "https://giddy-dinosaur-765.convex.site/api/auth/convex/jwks",
    },
  ],
};

Your deployment fetches a public key from that JWKS and checks a signature with it. That is the whole integration — which is why "lazy" costs you one file and why leaving it is cheap.

From then on, in any query or mutation:

const identity = await ctx.auth.getUserIdentity();

Still no second consent screen

Being hosted doesn't reintroduce the redirect. Your app talks to giddy-dinosaur-765.convex.site from its own origin over plain HTTP — it never navigates the user to an AussieAuth page — so signing in with Google shows Google's consent screen and nothing else. That's the same property the self-hosted path has, and the reason both exist.

What you're giving up

LazySelf-hosted
Three commandsFork, deploy, set credentials
No auth tables, no secretsYour database, your secrets
Methods the hosted deployment has set upEvery method, once you set its credential
Google's consent screen says AussieAuthIt says whatever your OAuth client says
Users live in someone else's deploymentUsers live in yours
Its uptime is your uptimeYours is yours

The last two are the real ones. If either matters, start lazy anyway and switch later — it's a one-line change to the issuer, and aussieauth init --self-hosted makes it for you.

Questions

Do I need a Convex deployment at all? For the sign-in card, no — it talks to AussieAuth over HTTP and imports no Convex. You need one the moment you want your own functions to know who the user is, which is what convex/auth.config.ts is for.

Which methods are on? Whatever the hosted deployment has credentials for. The card asks /apps/me on mount and draws only those, so a method that isn't set up is a button that was never there rather than a 403 you find by clicking.

Can I restrict which methods my app offers? Yes — that's the per-app allow-list, set when the app registers. See Using it from another app.

Is my app's data in the hosted deployment? No. Only the identity is. Your Convex deployment holds everything else and never sends it anywhere.

Read this page as markdown: /docs/lazy.md