Class: URISignature

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

Defined Under Namespace

Classes: ExpiredSignatureError, InvalidSignatureError, SignatureError

Constant Summary collapse

MINUTE =
60

Class Method Summary collapse

Class Method Details

.sign(uri, expiry: 5 * MINUTE, key: ENV['HMAC_SIGNATURE_KEY']) ⇒ String

Sign the given URI and return a signed URI.

Parameters:

  • uri (String)

    a valid URI

  • expiry (Integer) (defaults to: 5 * MINUTE)

    How many seconds before the signature expires.

  • key (String) (defaults to: ENV['HMAC_SIGNATURE_KEY'])

    The key used to sign the uri

Returns:

  • (String)

    The signed URI. The signature is added to the query params.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/uri_signature.rb', line 18

def self.sign(uri, expiry: 5 * MINUTE, key: ENV['HMAC_SIGNATURE_KEY'])
  uri = Addressable::URI.parse(uri)

  query_values = (uri.query_values || {}).to_a

  query_values << ["signature_expires", (Time.now + expiry).tv_sec]

  # Sort by the key to guarantee URI is always constructed in the same way
  query_values = query_values.sort_by { |k, v| k }

  uri.query_values = query_values

  add_signature(uri, key).to_s
  uri.to_s
end

.valid?(uri, key: , raise_error: true) ⇒ TrueClass, FalseClass

Is the given uri correctly signed.

Parameters:

  • uri (String)
  • raise_error (Boolean) (defaults to: true)

    Defaults to to true. Set this to false if you don’t this to raise but rather return false if it’s invalid. We default raise_error to true to be extra cautious as wanting to handle incorrectly signed callbacks is probably an edge case.

Returns:

  • (TrueClass, FalseClass)

    Will return true if correctly signed. Otherwise it will raise. If you really don’t want it to raise you can pass ‘raise_error: false`.

Raises:



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/uri_signature.rb', line 47

def self.valid?(uri, key: ENV['HMAC_SIGNATURE_KEY'], raise_error: true)

  # First remove the signature
  comparison_uri = Addressable::URI.parse(uri).clone.tap do |u|
    query_values = u.query_values.clone
    query_values.delete("signature")
    u.query_values = query_values
  end

  # Then recalculate and add back in the signature
  add_signature(comparison_uri, key)

  # Compare the uri to the original
  if secure_compare(uri, comparison_uri.to_s)
    expires = Time.at(comparison_uri.query_values["signature_expires"].to_i)
    if expires < Time.now
      raise URISignature::ExpiredSignatureError, "The signature for #{uri} has expired."
    end
    true
  else
    raise URISignature::InvalidSignatureError, "Invalid signature provided in #{uri}."
  end
end