Last active
June 25, 2019 22:56
-
-
Save apanzerj/da34a3739887719f466b to your computer and use it in GitHub Desktop.
A minimalistic signed request validation middleware
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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