Generates_token_for without block

I’m using the generates_token_for method in my Rails application for email verification. I’ve seen two different implementations and I’m not sure which one is correct or if there’s any difference in security:

Version 1 (with SecureRandom):

generates_token_for :email_verification, expires_in: 2.hours do
     SecureRandom.hex(20)
end

Version 2:

generates_token_for :email_verification, expires_in: 2.hours

My question is: Is it necessary to provide a custom block(something like: SecureRandom.hex(20)), or does generates_token_for already generate secure and unpredictable tokens by default?

I think the documentation explain this pretty well

A block may also be specified. When generating a token with TokenFor#generate_token_for, the block will be evaluated in the context of the record, and its return value will be embedded in the token as JSON. Later, when fetching the record with find_by_token_for, the block will be evaluated again in the context of the fetched record. If the two JSON values do not match, the token will be treated as invalid. Note that the value returned by the block should not contain sensitive information because it will be embedded in the token as human-readable plaintext JSON.

Purpose for this is to automatically invalidate a token for a record in certain conditions. For example you could provide a hash of the current encrypted password and email so a password reset token will be automatically invalidated if the password changes or the email changes

I am answering my question. After reading the source code for the feature and some PRs related to it on GitHub, I believe Version 1 (above) should never be used and it is completely wrong, since the block will keep generating new random strings and the token will always be invalid. The correct implementation is something along the lines of:

generates_token_for :email_verification, expires_in: 2.hours do
   email
end

This ensures that the token is for a specific email, and the email didn’t change between the token generation and validation.

The documentation literally says not to embed sensitive info

Note that the value returned by the block should not contain sensitive information because it will be embedded in the token as human-readable plaintext JSON .

I am not sure why you think SecureRandom.hex(20) is sensitive info. It is a randomly generated string, but using it seems wrong anyway. The documentation uses the last N characters of password salt as an example to store data that can be evaluated against the record context to validate the token.