Skip to content
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

feat: Change to using resetToken hash in DB #8041

Merged
merged 12 commits into from
Apr 26, 2023

Conversation

jaiakt
Copy link
Contributor

@jaiakt jaiakt commented Apr 12, 2023

Addresses #6855.
This PR makes it so we don't store raw resetTokens in our database but instead, the sha256 hash of the token. This makes it so if a database is compromised, an attacker can't steal resetTokens and reset users' passwords.

One catch with this PR is that the user defined handler() takes in a User object which it relies on for fetching the resetToken to generate a password reset link. Because now the User object will instead contain a resetToken hash, we need to make the raw token also available. I solve this by mutating the User object before calling the handler and explain this in the documentation for forgotPassword.handler(). This is potentially breaking if the application refetches the User object in the handler but not sure why they'd do that 🤔

Edit by Tobbe:
I've marked this as feature-breaking, but it's not really breaking. Just wanted to make sure that we highlight that any in-transit password resets will break when users upgrade RW to whatever version this is released in.
For low- to medium-traffic sites it's probably not a problem. Maybe just have an error message that asks users to try again if the token doesn't match.
For high-traffic sites, and/or for the best end-user experience, what developers can do is temporarily disable pw resets. Query their db and wait until no more valid reset tokens exist (by looking at resetTokenExpiresAt) and then upgrade and finally re-enable pw resets

@Tobbe
Copy link
Member

Tobbe commented Apr 12, 2023

Thanks for working on this @jaiakt!

I don't fully know the dbAuth code, so maybe I'm missing something obvious here, but how does the unhashed token get to the client?

Correct me if I'm wrong, but shouldn't the flow be like this?

  1. Create a pw reset token
  2. Hash the token and store in the DB
  3. Send the unhashed token to the client
  4. Client sends new pw + token to the dbAuth code
  5. token is hashed (again)
  6. if hashed token matches what's in the DB the pw is updated

@jaiakt
Copy link
Contributor Author

jaiakt commented Apr 12, 2023

Ah yeah I think that basically is the flow except for step 3. The backend application has to define a handler in which it typically sends an email to the user containing the token (in a password reset link). We don't send the token back to the client.
https://redwoodjs.com/docs/auth/dbauth#forgotpasswordhandler

@Tobbe
Copy link
Member

Tobbe commented Apr 12, 2023

Thanks 🙂 But still, that password reset link needs the unhashed token, right? With the code in this PR the handler gets the user object (if I understand the code correctly), and the user object only has the hashed token

@jaiakt
Copy link
Contributor Author

jaiakt commented Apr 12, 2023

Ohhh yes you're completely right! Let me think about this a little more

@jaiakt
Copy link
Contributor Author

jaiakt commented Apr 12, 2023

@Tobbe I don't love this solution but we can update the user's resetToken to have the raw token before calling the handler. I can update the docs to explain this behavior if it's not too hacky.

@Tobbe
Copy link
Member

Tobbe commented Apr 12, 2023

I like it! 🙂 This also makes this fully backwards compatible, right?

Except for tokens already stored in the DB I guess

@jaiakt
Copy link
Contributor Author

jaiakt commented Apr 12, 2023

Yup should be backwards compatible! Yeah tokens stored in the db probably won't be but they should expire and should be easily replaceable by using the forgot password flow again. Ty for the catch 😄

@Tobbe Tobbe added the release:breaking This PR is a breaking change label Apr 12, 2023
@Tobbe
Copy link
Member

Tobbe commented Apr 12, 2023

@jaiakt Can you take a look at the failing test case please?
image

@jaiakt
Copy link
Contributor Author

jaiakt commented Apr 12, 2023

@Tobbe just learned how to run tests locally lol. Fixed!

@Tobbe
Copy link
Member

Tobbe commented Apr 12, 2023

@Tobbe just learned how to run tests locally lol. Fixed!

I shouldn't have assumed you knew how to. Sorry

@jaiakt
Copy link
Contributor Author

jaiakt commented Apr 12, 2023

No worries! Ty for pointing out the broken test. All the CI checks are still a bit confusing to me

@jaiakt
Copy link
Contributor Author

jaiakt commented Apr 12, 2023

I noticed you tagged this as a breaking feature. Is this considered breaking because it should be backwards compatible?

@Tobbe
Copy link
Member

Tobbe commented Apr 12, 2023

No worries! Ty for pointing out the broken test. All the CI checks are still a bit confusing to me

Yeah, and unfortunately they're pretty flaky at the moment 🙁 So we might have to rerun them a few time to make them all pass

I noticed you tagged this as a breaking feature. Is this considered breaking because it should be backwards compatible?

I made an edit to your PR description trying to describe why I marked it breaking. It's not really breaking, I just wanted to make sure we added some extra info to the release notes for whatever version of RW this PR is released in

@jaiakt jaiakt force-pushed the jai-reset-token-hash branch from f53bafd to 4366e41 Compare April 14, 2023 00:13
@Tobbe
Copy link
Member

Tobbe commented Apr 14, 2023

This looks good to me, just want to get @cannikin's eyes on this too before merging

@Tobbe Tobbe enabled auto-merge (squash) April 26, 2023 15:32
@cannikin
Copy link
Member

Sorry for the wait! This look great, thanks again!

@jtoar jtoar disabled auto-merge April 26, 2023 17:33
@jtoar jtoar merged commit 08e092f into redwoodjs:main Apr 26, 2023
@redwoodjs-bot redwoodjs-bot bot added this to the next-release milestone Apr 26, 2023
@jtoar jtoar modified the milestones: next-release, v5.0.0 Apr 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release:breaking This PR is a breaking change
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants