Search code examples
rubyjwtsinatrarack

sinatra-cors not responding with 200 when using middleware


I am writing a little app for my own curiosity, and am having trouble with the sinatra-cors gem not responding with a 200. This causes Chrome to fail the options request (and subsequently not completing the things on the page).

I have the following set in both a Private Controller, and a Public controller. The Public controller handles routes that require no authentication (such as login), and the Private controller handles routes that require authentication via JWT.

In the Public controller, I have :

    enable :cross_origin
  end

  before do
    response.headers['Access-Control-Allow-Origin'] = '*'
  end

  # routes...
  options '*' do
    response.headers['Allow'] = 'GET, PUT, POST, DELETE, OPTIONS'
    response.headers['Access-Control-Allow-Headers'] = 'Authorization, X-Requested-With, X-HTTP-Method-Override, Content-Type, Cache-Control, Accept'
    response.headers['Access-Control-Allow-Origin'] = '*'
    200
  end 

The paths in the Public controller correctly return 200 for its options requests, since CORS is set to allow all domains. The paths in the Private controller fail with a 500, even though the same cors config is specified in the controller.

The difference, is that the Private controller, has a use JwtAuth statement at the start of the controller, which is the middleware that I have defined for using JWT tokens. When I remove this, the controllers pass the options request successfully.

The JWT middleware is as follows :


require 'json'
require 'jwt'

class JwtAuth
  CONTENT_TYPE = { 'Content-Type' => 'text/plain' }.freeze

  def initialize(app)
    @app = app
  end

  # rubocop:disable Metrics/MethodLength
  def call(env)
    options = { algorithm: 'HS256', iss: 'Jade-Liberty-Backend' }
    bearer = env.fetch('HTTP_AUTHORIZATION', '').slice(7..-1)
    payload, _header = JWT.decode(bearer, 'thereisnospoon', true, options)

    env[:scopes] = payload['scopes']
    env[:user] = payload['user']

    @app.call env
  rescue JWT::DecodeError
    [401, CONTENT_TYPE, [Errors::INVALID_TOKEN]]
  rescue JWT::ExpiredSignature
    [403, CONTENT_TYPE, [Errors::TOKEN_EXPIRED]]
  rescue JWT::InvalidIssuerError
    [403, CONTENT_TYPE, [Errors::INVALID_ISSUER]]
  rescue JWT::InvalidIatError
    [403, CONTENT_TYPE, [Errors::INVALID_ISSUED_AT]]
  rescue e
    [500, CONTENT_TYPE, [Errors::INTERNAL_SERVER_ERROR]]
  end
  # rubocop:enable Metrics/MethodLength
end

I followed the following to create the middleware: https://auth0.com/blog/ruby-authentication-secure-rack-apps-with-jwt/

I am unsure why adding the middleware causes the options request to fail.


Solution

  • The issue was related to an error in my middleware (Error module was not included in the source, and the default configuration of Rack caused errors not to be displayed)