Class: Hestia::MessageMultiVerifier

Inherits:
Object
  • Object
show all
Defined in:
lib/hestia/message_multi_verifier.rb

Instance Method Summary collapse

Constructor Details

#initialize(current_secret:, deprecated_secrets: [], options: {}) ⇒ MessageMultiVerifier

Public: creates a verifier for verifying against multiple secrets

API compatible with ActiveSupport::MessageVerifier once created. (initializer arguments are different.)

current_secret: [String] The current secret token, used for signing and verifying cookies deprecated_secrets: [String, Array] The previous secret token(s), used for verifying cookies only options [Hash] options to be passed to the verifiers - see ActiveSupport::MessageVerifier#initialze for details



14
15
16
17
18
19
20
21
22
23
# File 'lib/hestia/message_multi_verifier.rb', line 14

def initialize(current_secret:, deprecated_secrets: [], options: {})
  @current_verifier = build_verifier(current_secret, options)
  @deprecated_verifiers = Array(deprecated_secrets).map { |secret| build_verifier(secret, options) }.freeze

  # Generate these here so they are static when accessed in #verify
  @verifiers = [current_verifier, *deprecated_verifiers].freeze

  # Don't need to change any attributes, so why stay mutable?
  freeze
end

Instance Method Details

#generate(value) ⇒ Object

Public: generate a signed message

Uses only the ‘current_secret` to generate this.

See ActiveSupport::MessageVerifier#generate for more information

Returns String



32
33
34
# File 'lib/hestia/message_multi_verifier.rb', line 32

def generate(value)
  current_verifier.generate(value)
end

#verify(signed_message) ⇒ Object

Public: verify a signed message and convert back to original form

See ActiveSupport::MessageVerifier#verify for more information

Returns deserialized value Raises ActiveSupport::MessageVerifier::InvalidSignature



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/hestia/message_multi_verifier.rb', line 42

def verify(signed_message)
  errored_verifier_count = 0

  # Make sure we check *all* verifiers, every time we're called, to prevent timing attacks.
  # We run in a consistent amount of time no matter which (or if any) verifiers return a valid result
  results = verifiers.each_with_object([]) do |verifier, values|
    begin
      values << verifier.verify(signed_message)
    rescue ActiveSupport::MessageVerifier::InvalidSignature
      errored_verifier_count += 1
    end
  end

  # If all the verifiers errored, then none of them thought the message was valid
  if errored_verifier_count == verifiers.size
    raise(ActiveSupport::MessageVerifier::InvalidSignature)
  else
    results.first
  end
end