Ask HN: What do you use to build auth?
How do you build auth for your SaaS apps?
Do you use a library like NextAuth? Do you use a provider like Auth0 or Supabase Auth? Do you build your own auth?
I'm building an app using Next.js and Prisma. I'm currently considering Supabase Auth, Auth0, or writing the auth myself. People keep telling me that writing auth myself is a bad idea, and creating truly secure auth is really hard. Although I tried implementing auth with Supabase Auth, and I tried writing my own auth with Google OAuth and Magic Links, and my own auth ended up feeling much nicer and simpler.
I'm looking for some advice from more experienced people. What do you use? What would you recommend? What are the pros and cons of various approaches?
140 comments
[ 3.5 ms ] story [ 202 ms ] threadI tend to use one API endpoint for pass phrase authentication, which returns a token (any sufficiently complex unique string, such as base64 (or hex) of a >128 bit unique key . Many auths use a hash, to which I say don’t get carried away)
The token is a temporary throw away credential that should:
* expire (say 8 hours)
* times out after inactivity (say 2 hours)
* unique to session (allows distinction among concurrent sessions). I hash the token to create a unique session ID for logging
I only store these in cache, not the db so they expire and cannot be recovered (hashed again to create a trackable unique value.)
I ended up choosing https://userfront.com/
I think it has the best DX.
Truth be told, I usually hate having to do it. That said I’ve never found a reasonable alternative.
There are usually differences in each set up that require customisation (for example magic links like you mentioned) and trying to integrate a platform like auth0 feels like it would be as much work.
I also don’t like the idea of the inevitable migration when I need something else it doesn’t offer, it goes out of business or gets acquired.
Do you know any libraries that make this easy ? Looking for this myself using Django.
https://github.com/aaugustin/django-sesame
I've heard people say that writing your own auth is bad and hard, and the same people fucking it all up with a misconfigured Apache/Nginx server because they don't read/understand the documentation. Mistakes do happen, do your research, discuss it with others and you will learn a lot while creating a secure system that was not that hard to implement in the first place.
If your application fits into Sandstorm's model of grains[3]_, then the security benefits of Sandstorm are many.
.. [1] https://sandstorm.io/ .. [2] https://pypi.org/project/djangolovessandstorm/ .. [3] https://docs.sandstorm.io/en/latest/using/security-practices...
I spent something like 3 weeks this year on refactoring related to auth systems -- it's endlessly frustrating to me and I blame the fact that there's no web standard for login
I avoid 3rd party providers like auth0 -- I believe that they're better at security than me, but 1) they're a large target, 2) they're solving a harder problem and 3) giving the keys to every user acct to auth0 et al feels like too large of a compromise to make on behalf of my users. (that said, if I'm breached, I've given data to a worse third party).
also not wild about oauth, esp from a consumer perspective -- too easy to expand scope later and demand privacy leaks in exchange for logging into your acct
I'd be interested to know points to migrate away from firebase/google dependencies. I struggle to use anything else as it's so easy to get all the social logins set up...
In my current project I do away with accounts completely and just email a link that sets a cookie for 30 or 90 days [1]. I think the Auth you choose highly depends on the money/privacy/sensitivity/fraud potential of your project.
[1] https://tedpiotrowski.svbtle.com/shade-map-pro
Currently I am trying to integrate Firebase into AWS because of how bad Cognito is.
One thing going well that Cognito will never be is Firebase Auth.
The ease of two factor Auth is nice though.
At a high level:
1. Claim and prove ownership of a public key 2. Sign a session claim that links the handle with the public key 3. Service verifies the claim's signature was signed by the corresponding private key
I only wanted read-only social network login.
There was an official library in the language of my choice.
It was free (at my level of use).
The only thing I need to keep secure is the API tokens for Facebook / Twitter / GitHub etc. I don't even bother storing anything other than the user's ID, current name, and service.
If someone hacks my service, they won't be able to do anything nefarious with the user's data.
Building your own is a nightmare. If a login provider changes their API flow, Auth0 are incentivised to fix it - rather than passing the buck to me.
Yes the problem these days is a lot of commercial providers want this to become "common knowledge" and for it to be "best practice" to use their systems. But what happens if Okta has a data breach and you don't even know if your users were affected? (This happened)
I strongly believe in rolling your own auth:
1. It keeps it simple
2. You control the data that's a critical part of your system
3. Okta and Auth0 are giant targets because they have so many peoples' creds - you aren't
4. Even if you do use something like Auth0, you still need to implement authorization somehow. Of course, there are people who want to sell you solutions for that too...
Use libraries for the underlying primitives like bcrypt and JWT's. Do the rest yourself. Keep it simple.
Oh, and keep it simple.
The one good reason I've seen recently to use third party auth services is because they often support MFA. But how many companies actually need that? I've worked for a bunch of companies over my career and the amount of times I've had to implement/add MFA is 0.
Don't do your own cryptography (just use bcrypt or if you're confident you won't mess it up, libsodium), but authN/Z is entirely within the realm of "roll-your-own" and should be table stakes for most businesses these days.
Even adding SAML or OIDC support really isn't that hard.
For bigger projects where I need a full-fledged and reliable solution out of the box, I use Ory[1].
[0] http://www.passportjs.org/ [1] https://www.ory.sh/
I find Passport to be a dumpster fire of complexity and foot guns while Ory Kratos[0] + Ory Oathkeeper[1] have been the simplest way to add Auth to a project now (even for an MVP).
I've been using the zero trust proxy tutorial[2] and copy pasting the configs over. I just chuck some containers into Docker Compose and it goes. I don't even have to write the UI because Kratos does that but.
0: https://www.ory.sh/docs/kratos
1: https://www.ory.sh/docs/oathkeeper
2: https://www.ory.sh/docs/kratos/guides/zero-trust-iap-proxy-i...
I suspect most people use products like Auth0, not because they are trying to solve those hard problems, but more likely, because it's easy to set up and comforting to hand off that segment to a company that says they are battle-tested and secure, etc.
I personally like writing my own Auth because I can cater to my personal level of paranoia and learn more. Maybe I'm weird in that way, that I like to consider questions like, how short-lived should my token be? Should I issue a new token on every request? Local Storage vs cookie? Should I use a one-time token? Should I store the JWT in memory only on the client? How would I invalidate a user's token at will?
There's lots of interesting questions beyond the basics for sure and I can see how that might be intimidating. Most projects only need the basics though. And if you need more, it might be easier to extend your own implementation rather than some use-case not catered to by a library or service.
At the very least, I think it's wise for dev's to experience writing their own Auth a few times to get a grasp of some the challenges and better understand what those services might be doing for them.
You are presenting an extremely limited scope.
What you get from using an authentication provider is things like verifying email addresses, OAuth where you just have to "enable" Google, Github, Apple login possibilities. Implementing all these flows yourself is a lot more than 10 lines of code.
Then you also have to do password reset flows, resent email confirmation emails etc.
Most of the security nightmares I've heard of result from laziness or lack of care or someone not seeing a job through to the end. All of the encryption alogos we use will be easily brute-forced at some point in the future. Is it worse to store and back up those hashed passwords locally, rather than trusting Okta to do it for you (which incidentally was breached while your old MD5 hashes probably weren't)?
How hard is it? What are the challenges? Could you be more specific? Cause I keep seeing the blog and article headlines telling me how hard Auth is but a lot of them don't go into details. That's the kind of mystification I'm having an issue with.
I'm not against using service providers for CAPTCHA or CDN's like Cloudfront for DDOS/brute-force prevention. Refusing users the ability to register an account with breached credentials sounds like a great idea though I've implemented something like this myself already.
I'd wager that aside from what anyone put in their contracts with OKTA, that they didn't pay out a dime for the 25 minute Breach.
Do I want to just pass the buck for the sake of saying, I'm not personally liable or being able to deflect and say, "look this happened to a lot of other companies too". That's part of the fear that these blog posts and articles try to instill with absolutes of "never ever do this..." or "this is way too hard for you..."
I'd argue I'm less of an attack vector than Okta for most of my use-cases. And I think that's part of the issue here, that a lot of us are talking different scenarios and perspectives. If I was an IT head or sysadmin, yeah, I'd probably use something like Okta or onelogin. For a web app with an admin panel for a restaurant taking reservations, Yeah, I'd roll my own and own the users table with passwords as well. For an enterprise-level web app, I might consider compromising and use something like AWS Cognito. But for all my own personal projects, a few paid contractor jobs, and even a few large-scale apps, I have rolled my own auth and preferred it largely to alternative like firebase, and auth0. I'm starting to get the picture that I'm in the minority there. If you don't care about auth and don't want the liability, sure go with a provider. I did mention in my original comment that maybe I was weird in that I liked going down the rabbit hole on auth concerns.
Defeating credential stuffing attacks is HARD. That is where services such as Auth0 shine.
Minimizing a credential stuffing attack requires detecting the attack and then adding captchas or other bot mitigation techniques.
I obviously don't know how much it compares to your specific case, but as general advice it's often good advice when phrased less antagonistically: don't roll it yourself because the rabbit hole is deeper than you expect.
On that front, you may think you are paranoid enough, but keep in mind that the service products sometimes have a multiplier effect in play: N developers of various levels of paranoia, M people in various ops/InfoSec/auditing roles, X committees/layers designed to minimize corporate security risk.
If auth isn't your job and isn't your core competency, many times it is worth asking if following the rabbit down the hole is worth the time you could save outsourcing to a multiplier effect of a team that focuses 100% on those tasks. As with most "buy versus build" discussions, it's not just about "comfort" but leveraging those multiplier effects. Can you pay someone else for the years of experience and "rabbit hole wandering" and multiple layers of security audits? Auth is a "must have" feature for most products but in and of itself is mostly a security liability and mostly pure overhead: no customer is paying you directly for your auth features, auth is the annoying hurdle to features that they do care about and will pay you for. All that time spent on auth and auth security audits rarely appear to provide good returns on investment for your product and your customers. Time spent on auth is time wasted from user-facing features.
If auth isn't your job and isn't your core competency
I'd argue, it sort of is a core part of any web dev's job and any who neglect diving into it do so at their own risk. Maybe I come from a different time or place than what's the contemporary norm, though I'm okay with that. The main issue I have is with company-sponsored blogs and comments that will just blanket state you should never roll your own auth (because you can pay us instead, oh and btw, we rolled our own auth so you don't have to).
As a user, I subscribe to haveibeenpwned alerts on my main email addresses and have been for multiple years. I think there are fewer humbling "[hackers leaking my users table] won't happen to me" reminders than that. It happens to everyone. The ones listed in haveibeenpwned are just the tip of the iceberg and the ones we know about and have been confirmed/verified.
As an enterprise developer, I've been a part of some of the worst security audits imaginable. Thankfully those were all preventative industry requirements, and I'd hate to have to deal with "the real thing". Those audits always come with a reminder: the most secure data is data that you don't have, anything else it doesn't matter how well you think it is encrypted at rest is a liability.
As a user I never want to have to setup another password ever again. I realize that's a pipe dream and we are still at least a few standards away from making that dream a reality, but I believe in it and I've championed it when I've thought standards got close. (I was a fan of the original, now dead OpenID [but not the Zombie OIDC wearing its skin]. I thought Mozilla's Persona/BrowserID was brilliant and killed too early in its infancy. I'm hopeful for Webauthn.)
As a developer I never want to own another database table with passwords stored in it. It's low hanging fruit that every attacker is looking for. I don't care at this point how those passwords are salted, hashed, encrypted, mashed, or other waffle house preparation keywords. Attackers still try to pull the records no matter how safe you think they are. Not having them at all is just entirely easier than worrying about having them.
I don't entirely care how I get rid of the password table. I've paid for solutions. I've used passwordless tools like "magic links". (To use those right you often need all the hard lessons of JWTs and other token types and keeping those secure. It's not necessarily "easier" or "harder", but a lot of knowing how to do it right transfers.)
I just believe pretty strongly at this point that the best password table is one that you don't have and don't have to manage. No matter how easy it seems to "just bcrypt it".
I'm not quite to the point of never wanting to own another user table with a password column. Maybe I will be someday. Yes, it's a liability but I still think my table (which lives on RDS or Aurora btw) is less of a target than Okta's users tables, which were breached earlier this year. BTW, Auth0 was recently acquired by Okta.
But yeah, if I was spinning up a large-scale, enterprise-level web app, I'd probably tell my stakeholders we were going with AWS Cognito or something, just to offset liability. Never mind, I'm still going to have to have a users table, and if I'm using Cognito for authorization (group-level authorizers) then god help me (check out their limits on those custom fields too). And btw, they use JWT, and their front-end solution Amplify still uses local storage to hold credentials. And it's going to be even more difficult to set up the basic auth flow than if I rolled it myself, but who cares, as soon as the execs hear AWS solution or cloud-native, they nod and feel assured. After all, the liability is now somewhere else as you said. Never mind there's a host of missteps and other ways you could still mess it up on your side of the fence there.
For all of my personal projects, a few smaller paid side-projects, and at least two enterprise-level products, I've rolled my own and owned the DB and it's felt much better to me than the alternatives I've had to face when I forced into some auth as a service solution. I've done auth0 twice (never again). Firebase once. Cognito at least 4 times. I still prefer to roll my own. Maybe I am a masochist. Maybe I've been lucky. Maybe I just go against the grain and do things my own way, providing less of a common attack vector.
The number of devs I've run across who don't even understand the basics of authentication has surprised me. Yes, it's seemingly easy in concept as you say, to "just bcrypt it" but it astounds the number of devs I've come across or worked with who didn't even understand what that was or what it looked like. Then we have the "company-sponsored blogs" and the echo chamber of people throwing down absolutes of "whatever you do, never roll your own", "you don't understand how hard Auth is", etc...
But I'm coming to accept that maybe I'm in the minority on this topic so I'll stop ranting for now. I suspect that a lot of us on this thread are speculating or speaking from different scenarios and experiences, like a sysadmin or IT head, vs legacy enterprise apps, vs mobile, front-end specific SPA's with stateless auth, vs server-rendered stateful, session-based web apps, etc...
In any case I appreciate you taking the time to map out your thoughts and perspective in detail and I think it's probably broadened my own thoughts on the subject.
The problem is, you’ll slip up here and there. The experts recommend not rolling your own for a reason; little mistakes and gotchas like the details in your post can lead to significant compromise.
If there’s a use case to keep session tokens in localstorage, it’s insecure design that’s inherently vulnerable.
I use django-allauth: https://github.com/pennersr/django-allauth
But I'd still argue it's worth knowing what those libs are doing for you and you can only really appreciate it once you've rolled your own. I've done so on many a personal project where the risk was low. But yeah honestly, if I was rolling a large-scale enterprise app today, I'd probably utilize AWS Cognito. But this is also not for the faint of heart IMO.
It's a moved goal post which sneakily tries to position itself on top of the old adage "never roll your own crypto". Problem is, that adage is really about security whereas "don't roll your own auth" is about not wanting to do a mostly boring part of your job. I'd take my hat off if they'd admit it, but it's much easier to get budget with potential security scares compared to "we don't wanna".
[0] https://nextjs.org/docs/api-routes/introduction
1. Easy to implement 2. The burden of security is moved.
I use clerk.dev for almost any project at this point. They take care of auth/ authorization/ database sync.
Sure I could implement these on my own but why not use SSR for next and hook in and have everything setup in minutes so I can focus on the rest of the app that matters more.
Crypto is the thing I won't touch with a ten foot pole.
[1]: https://owasp.org/Top10/A01_2021-Broken_Access_Control/
I'm shocked.
FWIW, the majority of things on that list aren't what I'd consider covered by any auth library I know of.
It's mostly people just not using their authentication, diy or not.
[0] https://news.ycombinator.com/item?id=30700770
Have a good understanding of cryptographic primitives and what the state of the art is right now for hashing, salting; both at rest and in-transit, etc.
And make sure you follow the principles of least privilege.
Of course this is a fair bit of work and there are specialists in the industry who've made authentication into a service which takes care of following the guidelines and best practices... if you know how to vet them.
They don't always get it right either. Standards and practices evolve. Authentication (and its sibling authorization) require keeping up to date and being vigilant.
[0] https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.S...