I’ll hijack to ask a question that’s been on my mind…
I’ve heard people say “don’t write your own auth” but I’ve also spent a LOT of time hacking auth libraries to fit custom designs, custom flows, etc. And building tooling to integrate libraries with other infra: middleware, test mocks, etc.
My question is this: If you have a full time dev team maintaining an application, is it really that much slower in the long term to maintain your own auth flow?
I’ve basically given up on UI frameworks like Material UI because of this issue… if you’re building something customized, and you have full time devs to support it, the “time savings” of using a library like that is, in my opinion, a scam.
You feel like you’re getting a lot for free because the stuff that works out of the box is very visible. But the months and months of cumulative time spent dealing with the complexity that comes from customization and extension is invisible, because it’s just bugs you fix, or tasks that take a week instead of a day.
My team lost probably a dev month this quarter to react-select because of this. I’ve seen the same happen with Popper, react-table, and dozens of other supposedly helpful libraries.
I’m curious what other folks think about auth specifically though. Does auth fit into this pattern, or is it something that you really can meaningfully outsource to a library?
It depends on what context you're operating in. The reality is that most people don't fully understand authentication / authorization properly so they often mess up. When you have a small team of engineers that are spread very thin, it might be better to delegate this responsibility. If you have the time and resources to study the topic in depth and implement it properly then it's fine. It's just not that interesting of an area since the space for innovation and creativity is limited, and since the major problems have already been reliably solved by others at best you end up with an equivalent outcome and at worst you end up with security issues.
If you're operating within an enterprise context, Keycloak [0] is pretty massive but provides comprehensive coverage for all authN and authZ needs, and it's open source.
Back when I first started studying this topic I found that reading through a lot of NIST guidelines was helpful. I'd recommend at least browsing through SP 800-63-3 [1], SP 800-63A, SP 800-63B, SP 800-63C to get a good idea of the domain. Admittedly, this might be a lot of overkill for your application and needs.
Speaking from experience, what’s actually tricky about building your own auth isn’t the core functionality - there are lots of examples online - but rather all the things around authentication and user management that people don’t consider at first, but eventually become necessary when you go to production:
- password reset flows
- validating & cleaning inputs
- supporting multiple SSO methods for same account
- disconnecting or switching SSO methods
- handling duplicate accounts
- handling users who lose access to their login method
Don't listen to anyone saying "Don't write your own X". These things are not that hard once you filter out all the useless abstractions added by various libraries trying to give you a turn key solution. I suppose many just feel safe leaving responsibility in someone else's hands. I have never looked back after fighting passport.js some 10 years ago - being completely free to make you auth flow exactly how you need it is liberating! Now once you've done that a few times you are also in a much better position to decide if it might actually make sense to use a library or some auth provider for any future solutions.
I agree with your point in general, but I think passport.js and its ecosystem of plugins are very helpful. It’s lightweight and customizable for however you want to fetch and serialize sessions, and there are tons of examples online. Do you need it? No, but it makes it easier to standardize your login methods. A non-engineer on our team was able to build “login with linkedin” in less than a day just by following other examples in our codebase which is pretty powerful.
One thing about doing your own auth is you end up doing a lot of it. Integrations and expansion means more flows and schemes to do by hand. IMO setting up or buying a stable identity provider with all the bells and whistles is the way to go long term. Then you do your own client if you want but when you need to pull in another app they only need to work with your identity provider and not your code.
Build vs buy is a huge, complicated topic and it's more than just a technical conversation. A whole dev team is likely way more expensive than a SaaS for almost anything. You may think you can build it as a one-time cost and then maintenance will be cheap, but that's a trap. Every bug, every feature request is now on your queue and has to get prioritized against features that are actually core to your business. If you're the manager, how are you supposed to manage the risk of losing the team members who remember how it was built? You may think that hashing passwords is a solved problem so this isn't hard and that's also a trap. Building a fully-baked authentication platform is way more than just hashing passwords. You need all the UX flows for passwords, Oauth, email/sms recovery. Integration to your CRM or CDP. Obviously it's all solvable and possible, but why would you?
You can put the same logic behind loads of other enterprise software platforms. Building something like a CRM or Email management platform probably seems pretty easy, but if you look at what the SaaS products offer these days it's a mountain of features out of the box for a simple price tag. It's far, far less risky to buy than build.
Auth has been the target of a lot of SaaS and marketing. It's a menial simple task nobody wants to spend time on.
Even if you don't have a huge team I'd rather handroll over using third parties.
The mental effort of thinking about some other 3rd party developers or service is not worth it. I can write an auth system in under 100 lines and be on my way to ship features.
Social logins are the only annoying thing as they all use slightly different profile fields.
If you have issues with scale, scale vertically until it doesn't work anymore. By that point you'll likely have plenty of money to throw around for a registration team to worry about.
I’ve been in this situation before. I had a full dev team, we had a security architect assigned to us. We needed to do a pen test regardless of it we used a framework or wrote it from scratch.
Our requirements were quite unique and so writing custom code but aligning to an OIDC style flow was a good choice for us.
In the end it was a good choice and much easier than forcing an auth framework to do something it wasn’t designed to do.
We wanted to deploy into AWS lamba (using nextjs) with the main login flow being outside the app, then token validation being in a edge lambda. This decoupled the app from the authentication , we didn’t need any any authorisation.
If you use high level abstractions like authjs, you better use the happy path or it becomes a time sink. I think for authentication and authorization, use single purpose, well established libraries like bcrypt and passport.js and write the glue logic yourself. Sign up flow and authentication are too important to make concessions on.
I always understood the "dont write your own auth" as a much more low-level understanding of "auth", such as not writing your own password hashing (use bcrypt), not reinventing the wheel (dont design your own cookies / sessions, use a library, or do it like a library does it).
Ive never understood it as "dont make an api call to some auth yourself", because really, theres no problem with that (in my mind).
I've used things like django-allauth before and it's honestly not that hard to implement OAuth flows yourself and then you don't have to learn the ins and outs of someone else's API (i.e., allauth's numerous config settings).
Not sure this applies to Auth.js, but with hosted offerings, it's really hard to migrate off. Like how do you get the passwords into your system without forcing everyone to reset them?
> Like how do you get the passwords into your system without forcing everyone to reset them?
Disclosure: I work for an auth provider, FusionAuth.
I've written a number of migration guides and some hosted auth providers allow you to export password hashes (usually through a support ticket/out of band process; it's sensitive data). Others do not.
Definitely worth asking. It's your data, you should be able to get it.
Here are the ones I know that allow you to get your hands on password hashes:
* Auth0 (you have to be on a paid plan)
* Firebase
* FusionAuth (my employer, you get the whole encrypted database export)
Here are the ones that don't:
* Amazon Cognito
* Azure AD B2C
Once you have the hashes, it's a matter of ensuring that you can implement the same hashing algorithm so that the same user password entered into both systems ends up creating the same hash. Not rocket science, but sometimes, depending on the intricacies of the algorithm, can require a bit of spelunking. For instance, I was working on a keycloak migration guide and while both systems use Salted PBKDF2 with SHA-256, one used a 512-bit derived key and the other used a 256-bit derived key. I had to dig in a bit to figure that out.
It all depends on the degree of customization you do on your products, but if my team or I had to code all the auth or UI components by hand, I would quit the profession.
I speak from experience when I say that if there's something I like less than customizing MaterialUI component behavior it's working with a mangled custom-made replacement with worse semantics and functionality.
Disclosure: I work for FusionAuth, an auth provider.
I think of it like this: is the login and registration experience going to be a differentiator for your application? If so, how? Get real clear on the business value of a unique login experience (do you need to support special protocols? unique flows? pixel perfect UX control? multiple different user stores?).
If it isn't going to be a differentiator, then use a commercial or OSS solution that meets your needs, using the usual feature, standards and stability checkboxes.
If it is going to be a differentiator, evaluate the same options, but consider building as well. Much has been written about how devs underestimate the effort to build something ( https://www.google.com/search?hl=en&q=devs%20undersetimate%2... ), but I hear you, sometimes people underestimate how much effort can go into customizing something. Include, as best as you can, the features that are going to be 'differentiated', and see if customization or build from scratch is going to be more effort. (Spikes are helpful here, but to be honest, a lot of this pain is going to depend on future requirements that, being in the future, will be hard to foresee.)
If your auth flows are simple and easy to build, they'll probably be simple to customize. And if they are complex to customize, they'll probably be complex to build and support. TANSTAAFL.
You also want to think about maintenance. This of course cuts both ways. If you own the auth code, you control it and don't have to worry about breakages out of your control ( just like having your own C compiler: https://www.joelonsoftware.com/2001/10/14/in-defense-of-not-... ), but now you are responsible when a new feature or bugfix is needed, and it falls into your existing planning process. If you outsource this functionality, you still need to do integration testing, but you hopefully won't have to redo the work. (And you have a vendor to blame/hit over the head, which may or may not be helpful.)
Another way to put it is: should you build your own data storage system? Or use an RDBMS? Neither answer is always true, but for most applications, I'd lean towards the latter. I think the same is true with auth. It's a well understood space, with a lot of good solutions; finding one and using it will probably be the best option.
I’ve heard people say “don’t write your own auth” but I’ve also spent a LOT of time hacking auth libraries to fit custom designs, custom flows, etc. And building tooling to integrate libraries with other infra: middleware, test mocks, etc.
My question is this: If you have a full time dev team maintaining an application, is it really that much slower in the long term to maintain your own auth flow?
I’ve basically given up on UI frameworks like Material UI because of this issue… if you’re building something customized, and you have full time devs to support it, the “time savings” of using a library like that is, in my opinion, a scam.
You feel like you’re getting a lot for free because the stuff that works out of the box is very visible. But the months and months of cumulative time spent dealing with the complexity that comes from customization and extension is invisible, because it’s just bugs you fix, or tasks that take a week instead of a day.
My team lost probably a dev month this quarter to react-select because of this. I’ve seen the same happen with Popper, react-table, and dozens of other supposedly helpful libraries.
I’m curious what other folks think about auth specifically though. Does auth fit into this pattern, or is it something that you really can meaningfully outsource to a library?