Authentication
View as markdown.mdLocal development
Run authentication on your own machine, or point it at your real production users.
The Zeitlos CLI runs a local auth service, so sign-up, sign-in, verification and password reset all work on your machine without touching production.
npx @zeitlosapp/cli devRequires Node 22.5+. It writes ZEITLOS_AUTH_URL into .env.local for server-side use,
and the auth service listens on port 7311 by default.
Authentication emails
While using the emulator authentication emails won't be sent. Instead you'll see the link that
would be sent printed to the console where you started npx @zeitlosapp/cli dev.
Password resets
To test the password reset page, you'll need to configure the path locally in the zeitlos.dev.json file at the root of your repository. Set the auth.resetPasswordUrl to the URL that should
be used:
{
"auth": {
"resetPasswordUrl": "http://localhost:3000/reset-password"
}
}Testing against real users
Sometimes the emulator's isolated users aren't what you want and you'd rather sign in as a real deployed user:
{
"project": "my-app",
"auth": { "useProductionAuth": true }
}The local auth server then proxies to <slug>.ztls.app instead of emulating. Nothing in your
code changes — it still talks to the local emulator — but sign-in creates a real production
session and getUser() returns the real user.
Warning
Because this is production auth: real emails are sent, and their links point at your
production origin rather than localhost, so the terminal no longer prints them. The other
auth.* settings in zeitlos.dev.json are ignored. Your project's real settings apply.
Settings
Local development is configured in zeitlos.dev.json at your project root. These are the same
behaviours you'd set under Authentication → Settings in the dashboard, applied locally.
{
"auth": {
"enabled": true,
"signUpEnabled": true,
"requireEmailVerification": false,
"resetPasswordUrl": "http://localhost:3000/reset-password",
"verifyCallbackUrl": "http://localhost:3000/welcome"
},
"ports": { "auth": 7311 }
}| Key | Purpose |
|---|---|
auth.enabled | Can be used to disable the auth emulator. true by default |
auth.signUpEnabled | Allow sign-up |
auth.requireEmailVerification | Require verification before sign-in |
auth.resetPasswordUrl | Where the printed reset link points — reset stays disabled until set |
auth.verifyCallbackUrl | Where the verify link lands after verifying |
auth.useProductionAuth | Proxy to your real project's auth instead of emulating |
ports.auth | Run auth on a port other than 7311. Note: Changing the port requires more changes to your app outline below. |
Set resetPasswordUrl to your local reset page so the link printed to the terminal actually
lands somewhere. See Password reset.
Note
The file configures local development only. Nothing in it affects your deployed project. The database has its own keys in the same file; see Database → Local development.
Troubleshooting
Auth calls go to the wrong port
This is the one case the SDK cannot work out on its own. In the browser it can't read
ZEITLOS_AUTH_URL — that's a server-only variable — so it assumes the emulator's default port
7311. If you changed ports.auth, the browser keeps looking at 7311 and every call fails.
Tell the client where to look:
import { createClient } from '@zeitlosapp/sdk';
export const client = createClient({
// Unset in production, where same-origin resolution takes over — so the same
// code works in both places.
authUrl: process.env.NEXT_PUBLIC_ZEITLOS_AUTH_URL,
});Expose the port to the browser using whatever prefix your framework requires (NEXT_PUBLIC_,
VITE_, …):
NEXT_PUBLIC_ZEITLOS_AUTH_URL=http://localhost:7350The same applies to any dev host that isn't localhost.
forgetPassword throws RESET_PASSWORD_NOT_CONFIGURED
Reset is disabled until a reset page is configured. Locally that means setting
auth.resetPasswordUrl in zeitlos.dev.json — the dashboard setting only covers production.
Sign-in throws EMAIL_NOT_VERIFIED
requireEmailVerification is on. Either verify using the link printed to your terminal, or set
auth.requireEmailVerification to false while you build.