-
Notifications
You must be signed in to change notification settings - Fork 240
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Digest tokens and cache #344
base: master
Are you sure you want to change the base?
Digest tokens and cache #344
Conversation
… with digest tokens
Fix a comment issue
…models, not ActiveRecord::Base
…oken-authenticatable
…oken-authenticatable
…models, not ActiveRecord::Base
It looks like the Travis checks are failing due to incompatibility between the test gems and a very old version of Ruby being specified. Probably worth bring Travis version of Ruby up to something current and supported. |
@gonzalo-bulnes I have updated this PR to merge your recent changes. Is there any chance you will consider incorporating this? I believe this would be helpful for other security concious users of this gem. Equally, I understand if you don't have time for doing a lot with this gem. Is there anything I can do to help? |
I would definitely appreciate this functionality as well. |
@gonzalo-bulnes Any chance we can get this reviewed and merged? This PR addresses a pretty serious security vulnerability. |
@philayres Have you been using your branch (digests/hashing tokens) in production? I'm considering using what you've written here and am wondering if there were any gotchas or any yellow/red flags to consider prior to using this branch. Thanks for the work in putting together this PR! |
@davidalee sorry for the delay in responding. I have only used this in one production project (and not very widely). It doesn't appear to have caused any issues with regular logins, and the tokens seem to work fine. The caveat on this is that I only have a very limited number of API users, and have not tested with thousands of users. It is possible that there is a performance issue in doing so that I haven't tested. Feedback would be much appreciated! |
@gonzalo-bulnes - I see you have a lot of outstanding PRs in this project. I understand you are busy (aren't we all!) Any chance you could let us know if you plan to incorporate any of these contributions, or whether we should all just keep on forking and making our own changes? |
Hi @philayres, first of all, thank you for your persistence and patience. I've been thinking quite a bit about what to do here and reconsidering the discussion There are a few things in play here:
Putting that aside for a moment, the ideal scenario in my mind is that as many people as possible find support in their use cases, whatever those can be, and I am convinced that the free software model provides means to achieve that if we consider it collaboratively. I hear sometimes of forking in demeaning terms, I do not subscribe to that thinking. This code is published under a free software license with the intention of allowing it to be forked. Now, I also believe that there is more to collaboration than throwing code over the fence. That's why, if you are interested, I am open to thinking of ways of promoting your fork to people who are looking for hashed tokens. That could:
Getting into solution-space, I think that could take the shape of a "Similar projects" section in the README, or a pinned issue that people could add their forks / other gems to. (There are a few other projects addressing different uses of token authentication these days.) What do you think? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Little additional note, that I had been sitting on for some time now 🙂
BCrypt hashing is computationally expensive by design. If the configuration uses | ||
`config.sign_in_token = false` then the initial sign in is performed once per | ||
session and there will be a delay only on the initial authentication. If instead | ||
the configuration uses `config.sign_in_token = true` then the email and | ||
authentication token will be required for every request. This will lead to a slow |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the configuration uses
config.sign_in_token = false
then the initial sign in is performed once [...]
Isn't it the other way around? I don't see other mentions of sign_in_token
in this change set, so I assume it may be a typo? It may only be matter of flipping the condition around.
Naming is hard, and that's true for config options as well, but the intent in the current implementation is to be read as "if the token is used as a sign in token" (config.sign_in_token = true
) then sign in happens, which translates in a session being persisted. And if not (config.sign_in_token = false
), then there is no sign in and credentials must be provided with every request. (corresponding tests)
This PR relates to the discussion in issue #300 regarding the storage of plain text authentication tokens in the database.
The default operation of the gem has not been altered. Some configuration has been added to allow the developer to select to use digest rather than plain persistence of authentication tokens. The digest tokens are generated with the default Devise password hasher (BCrypt by default).
The challenge was not the generation or comparison of the digest tokens, but preventing a horrible slow-down. BCrypt is computationally expensive by design, to prevent somebody being able to brute-force the digests. For a stateless API this is no good, since there is no session and instead there is a reauthentication on each request. For this I implemented a cache mechanism, allowing an existing in-memory cache to be used to hold the previous authentication by a user.
I think that the update I have made to the README explains things best, so I'll quote here:
Overall it looks like there are lot of changes, but the reality is that most of this new stuff is around allowing the configuration of the cache in a clean way, plus some tests to show that it doesn't break things. To be honest, it was around the specs that I struggled most in trying to see if there was anywhere that the authentication is actually tested as a black box that I could extend. I may well have not followed your existing patterns perfectly here, but the tests do pass, and seem to have good coverage.
The other thing that I had to work around was the configuration. In a Rails app I used for testing the initializer file for configuration kept skipping the configurations for the ORM adapters, a model I attempted to follow with the cache provider. I have added a small callback after the configuration has been evaluated to attach the cache provider. Since I don't see how the Adapters get configured in the current setup, this may be an area worth discussing.
Anyway, I believe that this is a decent set of changes that meets the requirements of the discussion in the original issue. I considered separating out the changes into two PRs, one for digest persistence, and one for cache, but since they are so closely related in terms of needs it didn't make sense.
BTW I took the liberty of bumping the version (used in the gemspec) to 1.16.0, to ensure any builds reflect these changes. You should feel fee to own the actual numbering. Maybe this is worth a 2.0?
I'm having to spend some time getting this incorporated into my production systems this week (with tons of extra app testing) so I'm hoping that we can keep any changes to this PR well focused. I appreciate any feedback though in making it better and more secure!