Skip to content

Instantly share code, notes, and snippets.

@apanzerj
Last active June 25, 2019 22:56
Show Gist options
  • Save apanzerj/da34a3739887719f466b to your computer and use it in GitHub Desktop.
Save apanzerj/da34a3739887719f466b to your computer and use it in GitHub Desktop.
A minimalistic signed request validation middleware
# config/application.rb
require File.expand_path('../boot', __FILE__)
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module (YOUR APPLICATION)
class Application < Rails::Application
config.middleware.use "RequestValidator"
end
end
# app/middleware/request_validator.rb
class RequestValidator
def initialize(app)
@app = app
end
def call(env)
return @app.call(env) unless requires_signature?(env)
return fail unless signature(env).present?
puts "Signature: #{signature(env)}"
begin
valid_header = validate!(env)
puts valid_header if valid_header
rescue
return fail
end
@app.call(env) if valid_header
end
def fail
[422, {}, ["Invalid Request Signature"]]
end
def validate!(env)
payload = JWT.decode(signature(env), ENV['SIGNING_TOKEN'])
payload.first["signed"] == "(YOURAPP)" && payload.first["ts"].to_i > 5.minutes.ago.utc.to_i
end
def signature(env)
env['HTTP_X_(YOURAPP)_SIGNATURE']
end
def requires_signature?(env)
!!(env["PATH_INFO"] =~ %r{\A/api/subscribers})
end
end
# spec/middleware/request_validator_spec.rb
require 'rails_helper'
# largely lifted from http://taylorluk.com/post/54982679495/how-to-test-rack-middleware-with-rspec
RSpec.describe RequestValidator do
let(:app) { ->(env) { [200, env, "app"] } }
let :middleware do
described_class.new(app)
end
describe '#requires_signature?' do
it 'returns true for subscriber paths' do
expect(middleware.requires_signature?(env_for('/api/subscribers.json'))).to be true
end
it 'returns false for all other requests' do
expect(middleware.requires_signature?(env_for('/api/status.json'))).to be false
end
describe 'is true' do
describe 'with an invalid signature' do
it 'sends 422' do
code, env = middleware.call(env_for('/api/subscribers.json'))
expect(code).to be == 422
end
end
end
end
def env_for url, opts={}
Rack::MockRequest.env_for(url, opts)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment