Don't Roll Your Own Auth!
Last updated on June 23, 2025
I always hear it said online not to roll your own auth (usually it follows or precedes "Don't roll your own crypto"). Well I have been looking for an auth solution for ages, and I just can't seem to find a solution that I like. It's not like there's not a ton of them out there, but there's small (or big) problems with all of them. First, you have the big SaaS providers, and they are generous with their terms, but then you're locked in to their platform (a big non-starter for me). Then you have the big boys, KeyCloak, Authentik, and Dex (among others). These packages are huge. Further on down the chain you have a ton of small-ish packages that are built and popularized within a language's ecosystem- tons in Go, tons in Rust, but few are polished and continuously maintained.
So in my first foray, I went with KeyCloak. I spent nearly an entire workday reading about and planning my Keycloak deployment. Once it was time to fire it up, there was a lot of heavy lifting involved. I liked Keycloak, but it kept randomly failing at the worst times. I don't know if that's just the nature of KeyCloak or if it was my puny Digital Ocean box. I didn't have a lot of complaints with KeyCloak and am actually still kind of partial to it, but the login flows broke one too many times, so I went looking elsewhere.
My journey then led me to Authentik, which is a snazzy package that ships via a Docker container. It was super (and I mean SUPER) easy to set up. Within about 5 minutes, I had my client configured and my login flow working (admittedly it only required minor changes to my code from KeyCloak, so I saved a bunch of time there). Authentik is pretty. The admin side looks fantastic. Everything was well organized and it was easy to find the options I needed (and reminded me that I wished KeyCloak would stop changing their terminology in the UI every other point release). The whole thing with Authentik went so well that I didn't stop to ask myself if it was too good to be true. Well, drumroll, it was. Ooh boy was it ever. Within the first hour, I caught my box catching on fire with the cpu pegged at 99% and memory thrashing about. I could barely log in with 1 request per minute, let alone any kind of volume that a normal site would see. I was shocked. But, I should have known. Python, Django, Celery, Redis, Postgres. There's a reason they have to ship this mamma-jamma with Docker. Pretty as she was, I had to escort her out of my server and tell her not to come back. I honestly think it's a great package, but you need some horsepower to make it run well. Horsepower I'm not going to pay for - at least not right now.
So I'm back thinking about what in the world I'm going to use for auth on my puny box for my puny sites and ideas that don't actually earn any money, and I thought "eff it, I'll write it myself." Mistake? Only time will tell.
Remember what I said earlier about auth? Don't roll your own. Did I follow my and others' advice? Nope. But understand, I didn't set out to build anything flashy, I'm not trying to monetize anything, and my needs truly are minimal. So I made a deal with myself- do it right, right from the first line of code: always think about how you would break it; always pick the secure default; always think about security. I reasoned that if I did these things, that the resulting code would be solid.
I don't want to be KeyCloak, I don't want to run a SaaS business, I just wanted a small auth server that worked well on underpowered hardware. I figured, how hard could it really be to issue and validate tokens. Well, the truth is it's not that hard to do. What's hard to do is implement the other 999 things that need to be in an auth server. Of course most things are that way, but I spent probably 25% of my time on configuration options, 70% of my time on building a JSON/HTTP interface to it, and 5% of my time on the actual authentication part of it. But what I got in the end is pretty cool. It's just a tiny auth server, written in C# (but written in such a way as to enable ahead of time compilation), that supports HTTPS, runs a dual admin & authorization stack with a dual key architecture (which keeps that admin stuff on the admin side and the auth stuff on the auth side). It's not fancy, but it feels solid.
So the question is, does it actually work? Yup. Pretty well to be honest, and with minimal overhead to boot (57MB at idle). Does it support all of the features the big boys do? Nope. No 3rd party integration, no federation with other servers, and no configuring every single thing down to the last drop. Is that ok? Well, for my use case it's fine. Will it be ok for someone else's? Maybe, maybe not. I don't have a fancy web interface (edit: it does now), and I don't support login with Google or Github, so it might not be for a lot of people. But- for the people who want minimal with a cli interface? I think they'll find it's a decent piece of software.
So why do I get to roll my own? Well, you can too. But you must be serious. You must be deliberate. You must do a lot of research beforehand, and you should take it slow and do it right. I'm not special nor am I some 10x developer. But I am serious. Some people understand what that means, and some don't. But I took this seriously and I believe I wrote solid, production-ready code. Is it perfect? Nope. Is it bulletproof? I wouldn't run a bank on it- at least not yet, anyway. But I hope that people will use it and help improve upon it. Only then will it become something more than the sum of its parts.
The auth package supports:
- Dual-server architecture (admin & auth with separate signing keys)
- CLI & Web UI + REST (JSON/HTTP) admin
- AOT-compatible (.NET 8) runs on Linux/Windows/MacOS
- SQLite backend
- JWT + refresh + OTP + RBAC
- ~57MB @ idle, no surprise jobs running 24/7
- Swagger API docs
Welcome to the world microauthd! Check out the Github repo.
-padawan, 2025