Skip to content

Commit

Permalink
fix(rack): ensure OPTIONS preflight requests are handled
Browse files Browse the repository at this point in the history
  • Loading branch information
paulsturgess committed Nov 2, 2023
1 parent c328304 commit af24b05
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 5 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ gem 'rspec-mocks'

gem 'solargraph'

gem 'pry'
gem 'rake'
gem 'rubocop'
8 changes: 4 additions & 4 deletions examples/core_api/main_authenticator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ def call
cors.methods = %w[GET POST PUT PATCH DELETE OPTIONS]

# Define a list of cors headers that are permitted for the request.
cors.headers = %w[X-Custom-Header]
cors.headers = %w[Authorization Content-Type] # or allow all with '*'

# Define a the hostname to allow for CORS requests.
cors.origin = '*' # or 'example.com'
cors.origin = 'krystal.uk'

return if request.options?

given_token = request.headers['authorization']&.sub(/\ABearer /, '')
case given_token
when 'example'
if given_token == 'example'
request.identity = { name: 'Example User', id: 1234 }
else
raise_error 'CoreAPI/MainAuthenticator/InvalidToken', given_token: given_token.to_s
Expand Down
3 changes: 2 additions & 1 deletion lib/apia/rack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ def handle_request(env, api_path)

validate_api if development?

route = find_route(request_method, api_path)
access_control_request_method = env['HTTP_ACCESS_CONTROL_REQUEST_METHOD']&.upcase
route = find_route((access_control_request_method || request_method), api_path)
if route.nil?
Apia::Notifications.notify(:request_route_not_found, notify_hash)
raise RackError.new(404, 'route_not_found', "No route matches '#{api_path}' for #{request_method}")
Expand Down
45 changes: 45 additions & 0 deletions spec/specs/apia/rack_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,51 @@ def call(_env)
expect(Apia::Notifications).to have_received(:notify).with(:request_end, hash_including(path: 'test', method: 'GET', env: kind_of(Hash))).once
end

it 'should handle OPTIONS requests' do
controller = Apia::Controller.create('Controller') do
endpoint :test do
action do
response.add_field :hello, 'world'
end
end
end
auth = Apia::Authenticator.create('Authenticator') do
action do
cors.methods = %w[GET OPTIONS]
cors.headers = %w[Authorization Content-Type]
cors.origin = 'example.com'
next if request.options?

response.add_header 'x-executed', 123
end
end
api = Apia::API.create('MyAPI') do
authenticator auth

routes do
get 'test', controller: controller, endpoint: :test
end
end
rack = described_class.new(app, api, 'api/v1')
mock_request = Rack::MockRequest.env_for(
'/api/v1/test',
'REQUEST_METHOD' => 'OPTIONS',
'HTTP_ACCESS_CONTROL_REQUEST_METHOD' => 'GET'
)
result = rack.call(mock_request)
expect(result).to be_a Array
expect(result[0]).to eq 200

headers = result[1]
expect(headers['Access-Control-Allow-Methods']).to eq 'GET, OPTIONS'
expect(headers['Access-Control-Allow-Headers']).to eq 'Authorization, Content-Type'
expect(headers['Access-Control-Allow-Origin']).to eq 'example.com'
expect(headers['x-executed'].nil?).to be true

# assert body is empty (does not contain the response from the test endpoint)
expect(result[2][0]).to eq('""')
end

it 'should catch rack errors and return an error triplet' do
api = Apia::API.create('MyAPI')
rack = described_class.new(app, api, 'api/v1', development: true)
Expand Down

0 comments on commit af24b05

Please sign in to comment.