Auth Rolled: Part Deux

Created on June 27, 2025

Last updated on June 27, 2025


So it's been a week since I introduced microauthd to the world, and it's been a week of adding features, squashing bugs, and generally trying to make microauthd a real Identity Provider, just micro-sized. So what has gone on? Well, despite my better judgement I added a web admin interface and it shook the entire project's foundations. You see, when you are trying to do ahead-of-time (AOT) compilation with .NET Core, you have to follow very strict rules on what you can and can't do in your code- for example, reflection is off limits, so is runtime code generation, and a whole host of other features. Well, despite my best efforts, ASP.NET features were used that caused the AOT build to fail- hard. Honestly it was quite disappointing, because the AOT angle was one of the core selling points of microauthd. Alas I had to make a choice- significantly scale back and rewrite the admin interface, or give up AOT. I chose to give up AOT in the hopes that .NET 10 or beyond would fix the issues with [BindProperty] and that we could run as-is. I am still following the guidelines regarding what is and is not allowed in the core product, so if and when Microsoft ups their game, I will be ready.

In addition to the admin interface, I added a whole host of features and managed to keep the mad cli tool in lock-step with microauthd (they share a types .dll, so this is usually straightforward). The biggest features I've added are the two most recent, and that is caching of client secrets and caching of user password hashes (plus a rotating server pepper). I found that hashing these secrets should be ok because they are only stored in memory, and they are ephemeral when cached. The biggest surprise of all is that microauthd is not memory or disk limited, but cpu limited. It turns out argon2id is a beast (as it's designed to be, of course). A time factor of two, a parallelism of two, and a memory setting of 32MB can absolutely crush even my 16-core Ryzen desktop, so the caching was absolutely necessary to get any kind of reasonable performance. Bear in mind that microauthd isn't slow, I can churn through 1,000 requests and not drop a single one in about 17 seconds and that's with 50 concurrent requestors. No timeouts either. I'll be honest, I had much loftier performance goals, but this is my first foray into the auth game, so I really didn't know what to expect. Profiling shows the limitation is clear and it's name is argon. I could always offer scrypt, but OWASP recommends against that for new deployments, so I'm going to stick it out with Argon2id.

I also added support for static file hosting on the AUTH side of the fence so people could host their own login pages, I added Proof Key for Code Exchange support (PKCE), which allows a login flow to originate from devices or deployments where secret storage isn't safe or feasible (like a webpage). That went pretty well and a proof-of-concept example is now included in the repo. Additionally I went a little hog-wild with bindings for various languages to interface with both the ADMIN & the AUTH side- I did C# (of course), Go, Python and Javscript (Typescript really). I haven't had time to exhaustively test them, but they should run fine. Finally, I threw together a C# client library for bridging the gap between C#'s cookie based authentication and the token-based authentication of microauthd. I also included an example Razor Pages project that uses said client library. Edit- I have also bolstered the OIDC compliance by beginning to issue Id Tokens when the scope includes "openid".

At the end of the day, the journey continues. I honestly feel like microauthd is settling into a good place as a micro Identity Provider. The next steps will probably be implementing the client-side of OAuth2 and OIDC so that microauthd can integrate with other identity providers like Google and Github (among others). This would be about the last low hanging fruit to put into the system to really round it out.

A quick word about architecture- I took a lot of flack from a site-not-to-be-named about my lack of async code in the core product. Listen, I am no stranger to async code having been in the embedded game business for the bulk of my professional life. I use it where needed. Generally that's when you're doing high latency (or large) disk or network based I/O. Please understand that microauthd is already async by the virtue of running two kestrel servers, and that it is not I/O bound or network bound in any way. microauthd is purely cpu-bound, and that's a place where the behind-the-scenes state machines of async/await wouldn't help a lick. In fact, it might even hurt the code. Now if I decide to move and/or support alternate database systems, my take might change. But for now, it's synchronous code to a sychronous db in SQLite.

And a final note on the code- it's probably in the top 3 codebases I've created. It's clean, has a clear separation of concerns between layers, uses a strong and well-thought-out DTO system (in most spots; I can think of one where it wasn't, but that will get refactored). Adding and removing features is a breeze and can be done with surgical precision. Most codebases are not like that, but this one is, and we're pretty close to feature complete. Hacking on microauthd is fun and not a chore.

Now I need to get to "feature complete" for a v1.0 release and I've got a lot of documentation to write. Hopefully this effort will allow some adoption of microauthd, and I hope some people can find it useful. It's been a pleasure to write- a very rewarding experience. Let's hope she has a long and prosperous life!

    -Padawan, 2025