Starting with version 6.1,
ActiveRecord will support looking for records using signed IDs. This feature can be handy when doing things like resetting passwords, sending invitations, or doing email verification.
Signed IDs have three characteristics:
- They are tamper-proof
- They can be set to expire
- They can be scoped to a particular purpose
The above seems oddly similar to Signed Global IDs, so you may be wondering, why was this feature introduced?
The main difference is that
ActiveRecord signed IDs can only be used when referring to a single concrete class, conversely
GlobalId can be used when the passed ID might respond to any number of classes.
When Rails 6.1 is released all
ActiveRecord instances will respond to:
signed_idto generate the token
find_signedto find a record using the token
find_signed!same as the above but instead of returning
nilwhen the record is not found an error will be raised
Here's a simple example:
signed_user_id = User.first.signed_id User.find_signed(signed_user_id) => User.first
If you want to set a purpose and expiration to the token you can pass them as arguments to
signed_id, like this:
signed_user_id = User.first.signed_id(purpose: :email_confirmation, expires_in: 30.minutes.from_now) User.find_signed(signed_user_id) => nil User.find_signed(signed_user_id, purpose: :other) => nil User.find_signed(signed_user_id, purpose: : email_confirmation) => User.first
If you don't want the query to silently fail, you can use the bang-method alternative:
User.find_signed!("invalid_token") => ActiveSupport::MessageVerifier::InvalidSignature
Rails will automatically generate a
signed_id_verifier_secret to be used when encoding and decoding the tokens. You can also provide your own in an initializer.
# config/initializers/active_record.rb ActiveRecord::Base.signed_id_verifier_secret = "custom_verfifier_secret"
If you are interested in learning how this was implemented, take a look at the pull request where this feature was introduced.