class RooOnRails::Rack::PopulateEnvFromJWT

Constants

DEFAULT_MAPPED_URLS
UnacceptableKeyError
VALID_PREFIXES_KEY

Public Class Methods

configured?() click to toggle source
# File lib/roo_on_rails/rack/populate_env_from_jwt.rb, line 17
def self.configured?
  ENV[VALID_PREFIXES_KEY].present?
end
new(app, logger:, skip_sig_verify: true, url_mappings: DEFAULT_MAPPED_URLS) click to toggle source
# File lib/roo_on_rails/rack/populate_env_from_jwt.rb, line 21
def initialize(app, logger:, skip_sig_verify: true, url_mappings: DEFAULT_MAPPED_URLS)
  @app = app
  @logger = logger
  @url_mappings = url_mappings
  @keys = {}
  @mapped_urls = {}

  if skip_sig_verify && non_prod?
    @logger.warn "JWTs signature verifification has been switched off in development."
    @verify_sigs = false
  else
    @verify_sigs = true
  end
end

Public Instance Methods

call(env) click to toggle source
# File lib/roo_on_rails/rack/populate_env_from_jwt.rb, line 36
def call(env)
  env['roo.identity'] = decode_authorization_header(env['HTTP_AUTHORIZATION'])
  @app.call(env)

# Other exceptions will bubble up, allowing the higher middleware to return a 500, which is
# intentional.
rescue UnacceptableKeyError, JSON::JWT::Exception => e
  # Identifying user is clearly attempting to hack or has been given a totally incorrect
  # token, log this and flag as Forbidden, without executing the rest of the middleware stack.
  Raven.report_exception(e) if defined?(Raven)
  [401, {}, []]
end

Private Instance Methods

acceptable_key?(key_url) click to toggle source
# File lib/roo_on_rails/rack/populate_env_from_jwt.rb, line 77
def acceptable_key?(key_url)
  return false if key_url.nil?
  key_prefixes.any? { |acceptable| key_url.starts_with?(acceptable) }
end
decode_authorization_header(header_value) click to toggle source

@raise [UnacceptableKeyError,Faraday::Error,OpenSSL::OpenSSLError] From `#public_key` @raise [JSON::JWT::Exception] Bubble ups from `JSON::JWT.decode` @return [JSON::JWT] The list of claims this header makes by way of a JWS token. Will be an

empty hash for invalid or absent tokens.
# File lib/roo_on_rails/rack/populate_env_from_jwt.rb, line 68
def decode_authorization_header(header_value)
  return JSON::JWT.new unless (header_value || '').starts_with?('Bearer ')
  jws_token = header_value[7..-1]

  JSON::JWT.decode(jws_token, :skip_verification).tap do |jwt|
    jwt.verify!(public_key(jwt.header[:jku])) if @verify_sigs
  end
end
http_request() click to toggle source
# File lib/roo_on_rails/rack/populate_env_from_jwt.rb, line 103
def http_request
  Faraday.new do |conf|
    conf.response :json
    conf.response :raise_error
    conf.request :json

    conf.adapter Faraday.default_adapter
  end
end
key_prefixes() click to toggle source
# File lib/roo_on_rails/rack/populate_env_from_jwt.rb, line 51
def key_prefixes
  return [] unless self.class.configured?
  ENV[VALID_PREFIXES_KEY].split(',')
end
mapped_url(url) click to toggle source

Allows us to use internal URLs instead of external ones where appropriate

# File lib/roo_on_rails/rack/populate_env_from_jwt.rb, line 114
def mapped_url(url)
  return @mapped_urls[url] unless @mapped_urls[url].nil?

  @url_mappings.each do |prefix, replacement|
    next unless url.starts_with?(prefix)
    mapped = url.sub(prefix, replacement)
    @mapped_urls[url] = mapped
    return mapped
  end

  url
end
non_prod?() click to toggle source
# File lib/roo_on_rails/rack/populate_env_from_jwt.rb, line 56
def non_prod?
  if ENV['RACK_ENV'].nil?
    @logger.warn "Your RACK_ENV isn't set. You probably want it set to 'development' in dev."
  end

  %w(development test).include? ENV['RACK_ENV']
end
public_key(key_url) click to toggle source

@raise [UnacceptableKeyError] When the key URL is not from a trusted location @raise [Faraday::Error] When the JWK at the given URL is not retrievable for some reason.

See: https://github.com/lostisland/faraday/blob/master/lib/faraday/error.rb

@return [JSON::JWK] The JWK for the specified URL

# File lib/roo_on_rails/rack/populate_env_from_jwt.rb, line 86
def public_key(key_url)
  unless acceptable_key?(key_url)
    raise UnacceptableKeyError, "#{key_url} is not a valid Deliveroo Key URL"
  end

  # NB. don't use ||= memoization, or this middleware can be attacked by
  # being asked to decode large numbers of non-existant key-ids, each of
  # which would fill the @keys hash with a tuple.
  return @keys[key_url] if @keys.key?(key_url)

  @logger.info "Downloading identity public key from #{key_url}"
  json = http_request.get(mapped_url(key_url)).body
  @keys[key_url] = JSON::JWK.new(json)
rescue Faraday::ParsingError
  raise JSON::JWT::InvalidFormat, 'Downloaded JWK is not a valid JSON file'
end