aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/message_verifier.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support/message_verifier.rb')
-rw-r--r--activesupport/lib/active_support/message_verifier.rb41
1 files changed, 34 insertions, 7 deletions
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 7110d6d2c9..83c39c0a86 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
require "base64"
-require_relative "core_ext/object/blank"
-require_relative "security_utils"
-require_relative "messages/metadata"
+require "active_support/core_ext/object/blank"
+require "active_support/security_utils"
+require "active_support/messages/metadata"
+require "active_support/messages/rotator"
module ActiveSupport
# +MessageVerifier+ makes it easy to generate and verify messages which are
@@ -30,7 +31,7 @@ module ActiveSupport
#
# +MessageVerifier+ creates HMAC signatures using SHA1 hash algorithm by default.
# If you want to use a different hash algorithm, you can change it by providing
- # `:digest` key as an option while initializing the verifier:
+ # +:digest+ key as an option while initializing the verifier:
#
# @verifier = ActiveSupport::MessageVerifier.new('s3Krit', digest: 'SHA256')
#
@@ -73,7 +74,33 @@ module ActiveSupport
# Then the messages can be verified and returned upto the expire time.
# Thereafter, the +verified+ method returns +nil+ while +verify+ raises
# <tt>ActiveSupport::MessageVerifier::InvalidSignature</tt>.
+ #
+ # === Rotating keys
+ #
+ # MessageVerifier also supports rotating out old configurations by falling
+ # back to a stack of verifiers. Call +rotate+ to build and add a verifier to
+ # so either +verified+ or +verify+ will also try verifying with the fallback.
+ #
+ # By default any rotated verifiers use the values of the primary
+ # verifier unless specified otherwise.
+ #
+ # You'd give your verifier the new defaults:
+ #
+ # verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA512", serializer: JSON)
+ #
+ # Then gradually rotate the old values out by adding them as fallbacks. Any message
+ # generated with the old values will then work until the rotation is removed.
+ #
+ # verifier.rotate old_secret # Fallback to an old secret instead of @secret.
+ # verifier.rotate digest: "SHA256" # Fallback to an old digest instead of SHA512.
+ # verifier.rotate serializer: Marshal # Fallback to an old serializer instead of JSON.
+ #
+ # Though the above would most likely be combined into one rotation:
+ #
+ # verifier.rotate old_secret, digest: "SHA256", serializer: Marshal
class MessageVerifier
+ prepend Messages::Rotator::Verifier
+
class InvalidSignature < StandardError; end
def initialize(secret, options = {})
@@ -120,7 +147,7 @@ module ActiveSupport
#
# incompatible_message = "test--dad7b06c94abba8d46a15fafaef56c327665d5ff"
# verifier.verified(incompatible_message) # => TypeError: incompatible marshal file format
- def verified(signed_message, purpose: nil)
+ def verified(signed_message, purpose: nil, **)
if valid_message?(signed_message)
begin
data = signed_message.split("--".freeze)[0]
@@ -145,8 +172,8 @@ module ActiveSupport
#
# other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
# other_verifier.verify(signed_message) # => ActiveSupport::MessageVerifier::InvalidSignature
- def verify(signed_message, purpose: nil)
- verified(signed_message, purpose: purpose) || raise(InvalidSignature)
+ def verify(*args)
+ verified(*args) || raise(InvalidSignature)
end
# Generates a signed message for the provided value.