[HN Gopher] Rails authentication from scratch
       ___________________________________________________________________
        
       Rails authentication from scratch
        
       Author : clairity
       Score  : 43 points
       Date   : 2023-01-08 17:56 UTC (5 hours ago)
        
 (HTM) web link (stevepolito.design)
 (TXT) w3m dump (stevepolito.design)
        
       | throway2342444 wrote:
       | Rolling 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.
        
       | dorianmariefr wrote:
       | Great to see a post from a colleague at thoughtbot
        
         | clairity wrote:
         | i'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... =)
        
       | benevol wrote:
       | And for reference, the gold standard, Devise:
       | https://github.com/heartcombo/devise
        
       | jrvarela56 wrote:
       | A 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 wrote:
         | yah, 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/
        
       | benmmurphy wrote:
       | why 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 wrote:
         | I 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 wrote:
           | if you worried about a database compromise you can use the
           | blinding trick. so you expose secret to the user and store
           | HASH(secret).
        
       | mrinterweb wrote:
       | Authentication 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.
        
         | chucke wrote:
         | Devise has not aged well. It's quite complex for the
         | functionality it offers. Does not support json api mode. Only
         | supports email/password. It's account creation flow is too
         | simplistic for what most apps require OOTB.
        
         | debarshri wrote:
         | I 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 wrote:
           | Imagine 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.
        
             | debarshri wrote:
             | I dont think you need these features from day 1. My advice
             | should be taken with grain of salt. If the effort of
             | building these features is alot in your product, then yes
             | you should buy into a library or a service or a product.
             | But you also buy into the complexity these product bring
             | that might not be relevant for the project you are
             | building.
        
       | chucke wrote:
       | If 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/
        
         | jkmcf wrote:
         | I looked at it awhile ago but it appears to require Roda. Which
         | seems a bit heavy dependency on the surface.
        
           | chucke wrote:
           | Roda 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 wrote:
             | Yes, 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 wrote:
               | Roda 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 wrote:
           | The 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 wrote:
         | Rodauth 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.
        
       ___________________________________________________________________
       (page generated 2023-01-08 23:01 UTC)