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:
| Written | Why |
|---|---|
src/auth/Providers.tsx | The AussieAuth client and the Convex client, wired together |
| a sign-in route | <AussieAuthSignIn />, at the place your framework puts routes |
convex/auth.config.ts | Verifies 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
| Lazy | Self-hosted |
|---|---|
| Three commands | Fork, deploy, set credentials |
| No auth tables, no secrets | Your database, your secrets |
| Methods the hosted deployment has set up | Every method, once you set its credential |
| Google's consent screen says AussieAuth | It says whatever your OAuth client says |
| Users live in someone else's deployment | Users live in yours |
| Its uptime is your uptime | Yours 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.