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

Replace confirmation_token with signed_id #20

Closed
stevepolitodesign opened this issue Dec 12, 2021 · 4 comments · Fixed by #58 or #62
Closed

Replace confirmation_token with signed_id #20

stevepolitodesign opened this issue Dec 12, 2021 · 4 comments · Fixed by #58 or #62
Assignees

Comments

@stevepolitodesign
Copy link
Owner

I could probably drop a lot of the tables and just use signed_id

@stevepolitodesign stevepolitodesign self-assigned this Dec 12, 2021
@stevepolitodesign
Copy link
Owner Author

After looking at Devise and Clearance I'm not sure if this is industry standard. Both of these gems use columns to store tokens used for confirmations and remembering the user.

@edwinv
Copy link

edwinv commented Dec 28, 2021

When I read the README, I also thought about using signed_id instead of the columns. I believe it is still possible. signed_id has not been around for a long time, so many projects might not use it due to legacy. DHH actually promoted this for password reset and email verification.

For user validation and password reset, the following requirements are essential. These requirements are also in the OWASP Forgot Password cheat sheet.

  1. The reset/validation token should be only valid for a specific user
  2. The reset/validation token should expire after a certain amount of time
  3. The reset/validation token should expire when the wanted change happens, so either the password has changed, or the email is validated.

The default signed_id fulfills requirements 1 and 2. However, the third requirement is a bit more complex. To accommodate this, you probably need to use a custom MessageVerifier implementation.

verifier = ActiveSupport::MessageVerifier.new 's3Krit'
message = verifier.generate([user.id, user.updated_at], purpose: 'email_validation', expires_in: 10.minutes)

id, updated_at = verifier.verify(message)
user = User.find_by!(id: id, updated_at: updated_at)

As soon as something changes about the user, the token will invalidate, and the user needs to request a new one. You can also make this a bit more relaxed by only including the password_digest and email fields instead of the updated_at. However, be aware that the message's contents are not encrypted, so including the password_digest might be a security risk.

I still believe a message verifier is nicer to have than to include many token columns in the user model. Might be a nice improvement for your guide!

@edwinv
Copy link

edwinv commented Dec 28, 2021

A message verifier is also nice for the email update flow. You can use a message with the unconfirmed email address and send it to the user. When the user confirms with the message, you can update the database with the unconfirmed email address.

And the remember token can also be replaced with a signed_id.

@stevepolitodesign
Copy link
Owner Author

@edwinv thank you for the detailed response! Maybe I'll create a different branch that uses signed ids to highlight this approach since I think this would be valuable.

@stevepolitodesign stevepolitodesign changed the title Replace tokens with signed_id Replace confirmation_token with signed_id Jan 7, 2022
@stevepolitodesign stevepolitodesign linked a pull request Jan 7, 2022 that will close this issue
This was referenced Jan 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants