My Auth0 bill last month was $427. For 12,000 monthly active users on a side project that makes roughly $0 in revenue. I spent a Saturday moving off it. This is what happened.
The bill
Auth0's pricing jumps at the 1,000 MAU line. My project crossed it in March. The next tier is $240/month. Then I turned on MFA, which is extra. Then I wanted SAML for a B2B customer, which is extra. Then log retention went up because I needed 30 days for a compliance thing, which is extra.
$427. For a login button.
I like Auth0 as a product. I have shipped it at two previous companies and it was fine. What I did not like was paying rent on a feature I could own.
What I actually needed
I made a list before I started, so I would not spend a weekend building features I did not use.
- Email + password login
- Magic link login
- Password reset
- Session management with refresh
- OAuth with Google and GitHub
- A way to call our API from agent scripts (this one was new)
What I did not need, despite Auth0 charging for it:
- Multi tenant orgs
- SAML/SCIM (the B2B customer moved on)
- Passwordless WebAuthn (I want this, but not yet)
- 50+ social providers
- Rules and hooks for custom login logic (I had one rule, it moved to my app)
So the real question was: is there a library that covers the first list and lets me run it on my own Cloudflare Workers without rebuilding everything from scratch.
The shortlist
I looked at four things over maybe an hour.
Lucia got archived in March 2025. I did not want to adopt something the author shut down, even if the code still works.
Better Auth looked decent but ships a lot of plugins I did not want and the docs for its Cloudflare Workers story were thin when I checked.
NextAuth/Auth.js works but is tightly coupled to Next.js, and I needed this to cover a separate agent SDK as well.
kavachOS was the new one I had not tried. It does auth for humans and AI agents in the same library, runs on Workers, and the README had a "migrating from Auth0" page. That is the one I picked.
The 30 minute spike
I timed it. 32 minutes from pnpm add kavachos to a working login on localhost.
Install
pnpm add kavachos @kavachos/hono
I use Hono on Workers. There are adapters for Next.js, Express, Fastify, and a few others.
The core config
// src/auth.ts
import { kavachos } from "kavachos";
import { honoAdapter } from "@kavachos/hono";
export const auth = kavachos({
adapter: honoAdapter(),
database: process.env.DATABASE_URL!,
session: {
expiresIn: "30d",
rolling: true,
},
providers: {
password: { minLength: 12 },
magicLink: { tokenTTL: "10m" },
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
email: {
provider: "resend",
apiKey: process.env.RESEND_API_KEY!,
from: "noreply@myproject.com",
},
});
Wire it up
// src/index.ts
import { Hono } from "hono";
import { auth } from "./auth";
const app = new Hono();
app.route("/auth", auth.routes);
app.get("/me", auth.required, (c) => {
const user = c.get("user");
return c.json({ user });
});
export default app;
That is everything the server needs. Login, signup, magic link, OAuth callbacks, session refresh, logout, password reset, email verification. All mounted under /auth/*.
The frontend was about the same amount of code. A useSession() hook, a login form, a magic link form, an OAuth button component. Roughly 120 lines total across 4 files.
The schema
kavachOS ships migrations. I ran:
pnpm kavachos migrate
and it created the tables. users, sessions, oauth_accounts, password_reset_tokens, magic_link_tokens, email_verification_tokens. Six tables. I looked at them, they were boring in the right way.
The Auth0 migration
I had 12,000 users in Auth0. I needed to bring them over without forcing a password reset for everyone.
Auth0 exports users as JSON. The password hashes are bcrypt, which is what kavachOS uses internally, so in theory a direct copy would work.
# Export from Auth0 (this is an Auth0 built in job)
auth0 users export --format json --output users.json
# Import into kavachOS
pnpm kavachos import --from auth0 --file users.json
The second command does not exist. I wrote it that night after discovering the first one returns passwords as opaque hashes that Auth0 does not document the algorithm for. Or rather, they document it as "we use bcrypt" but the actual export format wraps the hash in a proprietary string.
This was the first thing that broke.
What broke in prod
The password hashes were not portable
Auth0's export includes a passwordHash field on each user. The format looks like a bcrypt string but it is prefixed with Auth0's own identifier. kavachOS would not match on login.
Fix: I wrote a small shim in my auth config that recognizes the Auth0 prefix, strips it, and re-verifies against the underlying bcrypt. After a successful login the user's hash gets re-saved in the native format. Over a week, 80% of active users migrated silently. The rest I handled manually with a one time "we need you to reset your password" email.
providers: {
password: {
minLength: 12,
verifyLegacy: async (hash, password) => {
if (hash.startsWith("$auth0$")) {
const actual = hash.replace(/^\$auth0\$/, "");
return bcrypt.compare(password, actual);
}
return false;
},
},
},
kavachOS has a verifyLegacy hook built in for this, which was nice. I did not have to patch the library.
Session cookies were on the wrong domain
I had cookies on auth.myproject.com from Auth0. kavachOS defaulted to myproject.com. This meant every logged in user got signed out at the cutover.
I picked up on this at 2am when my own login session died. Fix was a 10 minute config change plus an apology email.
session: {
expiresIn: "30d",
rolling: true,
cookie: {
domain: ".myproject.com",
secure: true,
sameSite: "lax",
},
},
Worth reading the session config docs before you deploy, not after.
Google OAuth redirect URI
Auth0 uses its own callback URL. My Google Cloud Console was configured for https://my-tenant.us.auth0.com/login/callback. kavachOS uses https://myproject.com/auth/callback/google.
30 second fix in the Google console, but it cost me an hour of "why is Google OAuth returning redirect_uri_mismatch" before I remembered.
The agent thing worked first try
The one unexpected win. My project has a handful of agent scripts that run on cron and need to hit the API as a specific user. With Auth0 I was using Machine to Machine tokens, which cost extra and have their own quota.
kavachOS has an agent identity primitive. You can mint an agent token, scope it to a user, give it permissions, and call the API. The docs are at kavachos.com/docs/agents. It took one line to wire up and replaced about $40/month of Auth0 M2M charges.
const agentToken = await auth.agents.issue({
userId: "user_123",
permissions: ["reports:read", "invoices:write"],
expiresIn: "90d",
});
This was the thing that made the migration actually worth it, not just a cost optimization.
The cost
Before: $427/month
After: $0/month for the library, ~$4/month for Neon Postgres that I was already paying for, ~$3/month for Resend.
If I ever want to stop running it myself, kavachOS has a managed cloud at kavachos.com with a generous free tier and metered pricing after that. I am not on it yet because running Workers + Postgres is cheap, but I like that the option exists.
Where Auth0 is still better
I want to be fair. Things Auth0 does well that I gave up:
- The dashboard. Auth0's admin UI is excellent. kavachOS has one but it is less polished.
- Enterprise SSO configuration. If you need to onboard a customer's Okta or Azure AD in an afternoon, Auth0's wizards are worth the money.
- Anomaly detection and breached password checks. kavachOS has the second, not the first.
- Log retention without a separate pipeline. I piped kavachOS logs to Axiom, which works, but it was a separate setup.
If your company is at the stage where any of those matter more than $5K/year, stay on Auth0. I am not at that stage.
Would I do it again
Yes, and I did it again last week for another project. The second migration took 45 minutes, mostly because I already had the verifyLegacy shim written and the agent identity stuff working. If you are reading this and your auth bill looks wrong, the math has changed in the last year. There are real libraries now.
If you try kavachOS, the docs I used most were the quickstart, the Auth0 migration guide, and the Hono adapter docs. The repo is at github.com/kavachos/kavachos.
Questions I expect
Is kavachOS production ready? I am running it on a real project with real users. Your call.
Why not just use Supabase auth? I did not want my auth tied to my database host. kavachOS works with any Postgres.
What about self hosting on Render or Railway? Works fine. The library does not care.
Did you consider Clerk or WorkOS? Clerk is good but more expensive than Auth0 at my tier. WorkOS is B2B focused and I am not that.
What if kavachOS gets abandoned like Lucia? The project is self hostable, the license is MIT, and the database schema is boring. If upstream went away tomorrow I would fork it and keep going. That is the whole point of picking open source over a managed service.
Following for the next one: I am writing these daily for the next two weeks. Tomorrow is magic link login in Next.js with no library, then with a library, with the actual diff. Hit follow if you want it.
If you have an Auth0 horror story, drop it in the comments. I will probably do a compilation post.
Gagan Deep Singh builds open source tools at Glincker. Currently working on kavachOS (open source auth for AI agents and humans) and AskVerdict (multi-model AI verdicts).
If this was useful, follow me on Dev.to or X where I post weekly.
This article was originally published by DEV Community and written by GDS K S.
Read original article on DEV Community