class AlexaVerifier::Verifier

A namespace for all of our verifiers to live under @since 0.1

Attributes

configuration[RW]

Public Class Methods

new() { |configuration| ... } click to toggle source

Create a new AlexaVerifier::Verifier object

@yield the configuration block @yieldparam config [AlexaVerifier::Configuration] the configuration object

# File lib/alexa_verifier/verifier.rb, line 14
def initialize
  @configuration = AlexaVerifier::Configuration.new

  yield @configuration if block_given?
end

Public Instance Methods

configure() { |configuration| ... } click to toggle source

Used to configure AlexaVerifier.

@example

AlexaVerifier.configure do |c|
  c.some_config_option = true
end

@yield the configuration block @yieldparam config [AlexaVerifier::Configuration] the configuration object

# File lib/alexa_verifier/verifier.rb, line 70
def configure
  yield @configuration
end
valid!(request) click to toggle source

Validate a request object from Rack. Raise an error if it is not valid.

@param [Rack::Request::Env] request a Rack HTTP Request

@raise [AlexaVerifier::InvalidCertificateURIError]

there was a problem validating the certificate URI from your request

@return [nil] will always return nil

# File lib/alexa_verifier/verifier.rb, line 29
def valid!(request)
  signature_certificate_url = request.env['HTTP_SIGNATURECERTCHAINURL']

  AlexaVerifier::Verifier::CertificateURIVerifier.valid!(signature_certificate_url) if @configuration.verify_uri?

  raw_body = request.body.read
  request.body && request.body.rewind # call the rewind method if it exists (handles Sinatra specifically)

  check_that_request_is_timely(raw_body) if @configuration.verify_timeliness?

  check_that_request_is_valid(signature_certificate_url, request, raw_body)

  true
end
valid?(request) click to toggle source

Validate a request object from Rack. Return a boolean.

@param [Rack::Request::Env] request a Rack HTTP Request @return [Boolean] is the request valid?

# File lib/alexa_verifier/verifier.rb, line 49
def valid?(request)
  begin
    valid!(request)
  rescue AlexaVerifier::BaseError => e
    puts e

    return false
  end

  true
end

Private Instance Methods

check_that_request_is_timely(raw_body) click to toggle source

Prevent replays of requests by checking that they are timely.

@param [String] raw_body the raw body of our https request @raise [AlexaVerifier::InvalidRequestError] raised when the timestamp is not timely, or is not set

# File lib/alexa_verifier/verifier.rb, line 80
def check_that_request_is_timely(raw_body)
  request_json = JSON.parse(raw_body)

  raise AlexaVerifier::InvalidRequestError, 'Timestamp field not present in request' if request_json.fetch('request', {}).fetch('timestamp', nil).nil?

  request_is_timely = (Time.parse(request_json['request']['timestamp'].to_s) >= (Time.now - REQUEST_THRESHOLD))
  raise AlexaVerifier::InvalidRequestError, "Request is from more than #{REQUEST_THRESHOLD} seconds ago" unless request_is_timely
end
check_that_request_is_valid(signature_certificate_url, request, raw_body) click to toggle source

Check that our request is valid.

@param [String] signature_certificate_url the url for our signing certificate @param [Rack::Request::Env] request the request object @param [String] raw_body the raw body of our https request

# File lib/alexa_verifier/verifier.rb, line 94
def check_that_request_is_valid(signature_certificate_url, request, raw_body)
  certificate, chain = AlexaVerifier::CertificateStore.fetch(signature_certificate_url) if @configuration.verify_certificate? || @configuration.verify_signature?

  begin
    AlexaVerifier::Verifier::CertificateVerifier.valid!(certificate, chain) if @configuration.verify_certificate?

    check_that_request_was_signed(certificate.public_key, request, raw_body) if @configuration.verify_signature?
  rescue AlexaVerifier::InvalidCertificateError, AlexaVerifier::InvalidRequestError => error
    # We don't want to cache a certificate that fails our checks as it could lock us out of valid requests for the cache length
    AlexaVerifier::CertificateStore.delete(signature_certificate_url)

    raise error
  end
end
check_that_request_was_signed(certificate_public_key, request, raw_body) click to toggle source

Check that our request was signed by a given public key.

@param [OpenSSL::PKey::PKey] certificate_public_key the public key we are checking @param [Rack::Request::Env] request the request object we are checking @param [String] raw_body the raw body of our https request @raise [AlexaVerifier::InvalidRequestError] raised if our signature does not match the certificate provided

# File lib/alexa_verifier/verifier.rb, line 115
def check_that_request_was_signed(certificate_public_key, request, raw_body)
  signed_by_certificate = certificate_public_key.verify(
    OpenSSL::Digest::SHA1.new,
    Base64.decode64(request.env['HTTP_SIGNATURE']),
    raw_body
  )

  raise AlexaVerifier::InvalidRequestError, 'Signature does not match certificate provided' unless signed_by_certificate
end