5 ms·
Rails authentication from scratch
- dorianmariefr 4y agoGreat to see a post from a colleague at thoughtbot
- clairity 4y agoi've admittedly only skimmed the (long) article for now, but it's a great reference and really shows how much thought needs to go into user authentication flows. i've done this kind of custom implementation in the past and it was definitely a great learning experience, but i'm not sure i'd trust my code to withstand a serious attack. they basically recreated a simpler but not-as-flexible (yet?) devise. the flexibility/adaptability of devise seems to be where all the extra complexity comes from. i'm just waiting to see this released as a gem, and the cycle to start anew... =)
- chucke 4y agoIf you want to use an authentication solution which will not make you want to roll your own and highly configurable, use rodauth: http://rodauth.jeremyevans.net/ http://rodauth.jeremyevans.net/
- jkmcf 4y agoI looked at it awhile ago but it appears to require Roda. Which seems a bit heavy dependency on the surface.
- chucke 4y agoRoda is as lightweight as it gets, when it comes to ruby web routers. If you're worried about heavy dependencies, you should probably look into rails first.
- jkmcf 4y agoYes, Rails is a heavy framework. But strapping another framework on top of rails just for authentication smells pretty bad. The reality may be different, but superficially it seems odd.
- chucke 4y agoRoda is not a full blown framework. The footprint of rodauth overall is quite low, when compared. Couple that with the fact that you get 2 factors, sms, otp, webauthn, json mode, argon2, along with json mode + an array of security and usability features, from a single dependency.
- nnf 4y agoThe website shows code examples for use in non-Roda applications, and the readme states: > Rodauth is Ruby's most advanced authentication framework, designed to work in any rack application.
- bvirb 4y agoRodauth is really great. We had a partially hand-rolled solution and it was time to add MFA so decided to migrate rather than build more of our own authentication logic, it was definitely the right call.
- benevol 4y agoAnd for reference, the gold standard, Devise: https://github.com/heartcombo/devise https://github.com/heartcombo/devise
- jrvarela56 4y agoA great reminder that, if you choose to roll your own auth, you need to handle a lot of edge cases. Timing attacks are not obvious and easy to forget protecting against.
- clairity 4y agoyah, i ran across this article[0] that had some response time comparisons with the new authenticate_by feature in the upcoming rails 7.1 that mitigates a specific timing attack, which is what got me interested in seeing what the rails auth landscape looked like now. [0]: https://blog.kiprosh.com/rails-7-1-adds-authenticated_by/ https://blog.kiprosh.com/rails-7-1-adds-authenticated_by/
- Spivak 4y agoOutsourcing your auth suuuuuucks. It is such a pita when like the most critical service to uptime lives outside your control and so painful to switch. Don’t write your own, but also own it, and completely abstract it. Don’t let anything but a single file in a single shared library or service know anything at all about the underlying implementation.
- mrinterweb 4y agoAuthentication is rarely something I want to write myself. There are many security concerns that need to be considered, many that I forget about. Libraries like devise are well thought out, and have been security hardened. I'd much rather use an established authentication library or service than roll my own. At higher scales, I've written authentication middleware services when performance was critical and we had complex auth needs, but as a starting place for a new app, I would not roll my own.
- debarshri 4y agoI think it really depends. Sometime you want to have control over the process, and if you have experience building authentication before i would recommend doing it yourself rather than working with an abstraction defined by a library. I have seen first hand with tools like fusion auth when you hit the situation which cannot be resolved while adding more complexity to your product, I would rather start building it into the product than increasing the product complexity and working with an abstraction you dont have control over. Again take advice with grain of salt.
- bvirb 4y agoImagine you want to stop someone from brute-force password attempts on your users. You could add some code to lock a user out after a specific number of attempts within a given time period for that username. But then say it's a real user and they forgot their password and they want to get back in without having to wait. You might then allow them to reset their password (which requires re-verifying access to their email addresses) and immediately reset the lock so they can get back in. But then since that functionality technically allows anyone to have your app send a password reset email to any of your users without authentication you might want to limit the rate at which those resets can be requested. None of that is particularly hard, but it's a lot to build, test, and maintain. Start throwing in more account features you might take for granted elsewhere (MFA, recovery codes, re-enter current password to change your password, allow a user to sign out of all their sessions, etc etc) and you could find yourself spending all your time building your user system instead of your application features. I would guess, for most applications, user systems are standard enough that rolling your own isn't going to be worth the possible additional customization.
- benmmurphy 4y agowhy do people use cryptographically signed tokens for secret tokens (reset password / confirmation)? it should be safer to generate a random string and if the token needs to expire to store a timestamp in the database. revocation can then be performed on individual tokens. I can see why people might be more tempted to use these crypto-tokens for session storage to avoid the database but I think if this is a requirement then you should be using redis or some other fast key storage to hold the sessions. if you are worried about timing attacks then you can just have your routes in the form of `/user/<id>/reset_password/<token>` and pull the user out of the DB and then do the comparison using a timing safe function. alternatively, if that is not possible and you need to do a lookup by the token its possible to blind the value stored in the DB but i would avoid that because it introduces another cryptographic primitive unnecessarily. and having only index on user_id and not having to have indexes on all your tokens saves space as well!
- aobdev 4y agoI think the most secure approach is to store a random id in the database and to also sign it any time it’s exposed to the user (as a cookie or url sent via email). The first benefit is that you won’t be exposed to DoS attacks whereby you’re querying non-existent tokens from your database, and the second benefit is that illegitimate sessions can only be established by compromising both your database sessions table and your application’s signing secrets.
- benmmurphy 4y agoif you worried about a database compromise you can use the blinding trick. so you expose secret to the user and store HASH(secret).
- throway2342444 4y agoRolling your own auth is a waste of time unless this is some learning exercise. Cognito + oauth2-proxy or the millions of alternatives are at your disposal. Although for past 15 years I've socially distanced myself from Rails, I'm pretty sure it had a decent auth library at the time, something called device.