Module: ActiveRecord::SignedId::ClassMethods
- Defined in:
- lib/active_record/signed_id.rb
Instance Method Summary collapse
-
#combine_signed_id_purposes(purpose) ⇒ Object
:nodoc:.
-
#find_signed(signed_id, purpose: nil) ⇒ Object
Lets you find a record based on a signed id that’s safe to put into the world without risk of tampering.
-
#find_signed!(signed_id, purpose: nil) ⇒ Object
Works like find_signed, but will raise an
ActiveSupport::MessageVerifier::InvalidSignature
exception if thesigned_id
has either expired, has a purpose mismatch, is for another record, or has been tampered with. -
#signed_id_verifier ⇒ Object
The verifier instance that all signed ids are generated and verified from.
-
#signed_id_verifier=(verifier) ⇒ Object
Allows you to pass in a custom verifier used for the signed ids.
Instance Method Details
#combine_signed_id_purposes(purpose) ⇒ Object
:nodoc:
92 93 94 |
# File 'lib/active_record/signed_id.rb', line 92 def combine_signed_id_purposes(purpose) [ base_class.name.underscore, purpose.to_s ].compact_blank.join("/") end |
#find_signed(signed_id, purpose: nil) ⇒ Object
Lets you find a record based on a signed id that’s safe to put into the world without risk of tampering. This is particularly useful for things like password reset or email verification, where you want the bearer of the signed id to be able to interact with the underlying record, but usually only within a certain time period.
You set the time period that the signed id is valid for during generation, using the instance method signed_id(expires_in: 15.minutes)
. If the time has elapsed before a signed find is attempted, the signed id will no longer be valid, and nil is returned.
It’s possible to further restrict the use of a signed id with a purpose. This helps when you have a general base model, like a User, which might have signed ids for several things, like password reset or email verification. The purpose that was set during generation must match the purpose set when finding. If there’s a mismatch, nil is again returned.
Examples
signed_id = User.first.signed_id expires_in: 15.minutes, purpose: :password_reset
User.find_signed signed_id # => nil, since the purpose does not match
travel 16.minutes
User.find_signed signed_id, purpose: :password_reset # => nil, since the signed id has expired
travel_back
User.find_signed signed_id, purpose: :password_reset # => User.first
42 43 44 45 46 47 48 |
# File 'lib/active_record/signed_id.rb', line 42 def find_signed(signed_id, purpose: nil) raise UnknownPrimaryKey.new(self) if primary_key.nil? if id = signed_id_verifier.verified(signed_id, purpose: combine_signed_id_purposes(purpose)) find_by primary_key => id end end |
#find_signed!(signed_id, purpose: nil) ⇒ Object
Works like find_signed, but will raise an ActiveSupport::MessageVerifier::InvalidSignature
exception if the signed_id
has either expired, has a purpose mismatch, is for another record, or has been tampered with. It will also raise an ActiveRecord::RecordNotFound
exception if the valid signed id can’t find a record.
Examples
User.find_signed! "bad data" # => ActiveSupport::MessageVerifier::InvalidSignature
signed_id = User.first.signed_id
User.first.destroy
User.find_signed! signed_id # => ActiveRecord::RecordNotFound
62 63 64 65 66 |
# File 'lib/active_record/signed_id.rb', line 62 def find_signed!(signed_id, purpose: nil) if id = signed_id_verifier.verify(signed_id, purpose: combine_signed_id_purposes(purpose)) find(id) end end |
#signed_id_verifier ⇒ Object
The verifier instance that all signed ids are generated and verified from. By default, it’ll be initialized with the class-level signed_id_verifier_secret
, which within Rails comes from the Rails.application.key_generator. By default, it’s SHA256 for the digest and JSON for the serialization.
71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/active_record/signed_id.rb', line 71 def signed_id_verifier @signed_id_verifier ||= begin secret = signed_id_verifier_secret secret = secret.call if secret.respond_to?(:call) if secret.nil? raise ArgumentError, "You must set ActiveRecord::Base.signed_id_verifier_secret to use signed ids" else ActiveSupport::MessageVerifier.new secret, digest: "SHA256", serializer: JSON end end end |
#signed_id_verifier=(verifier) ⇒ Object
Allows you to pass in a custom verifier used for the signed ids. This also allows you to use different verifiers for different classes. This is also helpful if you need to rotate keys, as you can prepare your custom verifier for that in advance. See ActiveSupport::MessageVerifier
for details.
87 88 89 |
# File 'lib/active_record/signed_id.rb', line 87 def signed_id_verifier=(verifier) @signed_id_verifier = verifier end |