Using CORS with Rails 4 and heroku - heroku

I'm using Rails 4 on heroku and need to enable CORS.
I've looked around the web for hours now and tried various solutions, which worked for many other people, but not for me.
My last try was to simply add CORS to all requests in the application controller:
before_filter :cors_preflight_check
after_filter :set_headers
def set_headers
#if request.headers["HTTP_ORIGIN"]
# better way check origin
#if request.headers["HTTP_ORIGIN"] && /^https?:\/\/(.*)\.(.*)\.cloudfront\.net$/i.match(request.headers["HTTP_ORIGIN"])
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
headers['Access-Control-Allow-Headers'] = %w{Origin Accept Content-Type X-Requested-With auth_token X-CSRF-Token}.join(',')
headers['Access-Control-Max-Age'] = "1728000"
#end
#end
end
def cors_preflight_check
if request.method == "OPTIONS"
headers['Access-Control-Allow-Origin'] = 'http://localhost'
headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
headers['Access-Control-Allow-Headers'] = %w{Origin Accept Content-Type X-Requested-With auth_token X-CSRF-Token}.join(',')
headers['Access-Control-Max-Age'] = '1728000'
render :text => '', :content_type => 'text/plain'
end
end
The commented lines were a try to make all of it more clean. However, nothing of this works. This is how this thread told me to do.
Do you have any idea or hint how I can get this to work?
Thanks in advance!

So you have just about everything I have but I don't think you're matching the options request to your cors_preflight_check method. You can also remove the before filter on cors_preflight request since options will be routed there by default. And if you have any authentication for requests it needs to be removed for options requests. Check this out https://github.com/cleor41/Cors-Rails4-API and I'll post the important parts here.
This goes in the application controller for your API
#APIController
before_action :authenticate_user
after_filter :cors_set_access_control_headers
skip_before_filter :authenticate_user, :only => [:route_options]
def route_options
cors_preflight_check
end
private
def authenticate_user
#Do some cool stuff with tokens to identify the user
end
def cors_set_access_control_headers
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, PATCH, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization, Token, Auth-Token, Email'
response.headers['Access-Control-Max-Age'] = "1728000"
end
def cors_preflight_check
if request.method == 'OPTIONS'
request.headers['Access-Control-Allow-Origin'] = '*'
request.headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, PATCH, DELETE, OPTIONS'
request.headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-Prototype-Version, Token, Auth-Token, Email'
request.headers['Access-Control-Max-Age'] = '1728000'
render :text => '', :content_type => 'text/plain'
end
end
This goes in your routes and matches to the cors_preflight_check in your api application controller
#Routes
#Route this to wherever you put the cors_preflight_check
#This is to handle the CORS preflight request, it only catches the options action.
controller 'api/v1/api' do
match '*unmatched_route', :to => 'api/v1/api#route_options', via: [:options]
end
Hope this works for you, let me know if it doesnt.

Related

HTTParty post method doesn't return status code when adding follow_redirects = false along with other headers and cookies?

The HTTParty methods are like HTTParty.XXX(urlGoesHere, and Args Here)
I'm doing the following:
params = {:UserName => "uname", :Password => "pwd"}
cookie_hash = HTTParty::CookieHash.new
cookie_hash.add_cookies("key1=val1")
cookie_hash.add_cookies("key2=val2")
options = {
:body=>params,
:headers => { 'Cookie' => cookie_hash.to_cookie_string,
'Accept' => something },
:follow_redirects => false
}
URLUserNamePwd = HTTParty.post(myURL, options) # Is this the right way to do?
When I check the http status code, body I get nothing. When I check in the browser development console, I see 302 redirect, and in response headers I see lot of header pairs returned.
What is the URL you're attempting to hit? 302 Found will be paired with a Location header containing the URL where the resource can be found.

sinatra-cors not responding with 200 when using middleware [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 3 years ago.
Improve this question
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.
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)

Ruby HTTP sending API key Basic_auth

I have been following a tutorial on GitHub Pages and
I am trying to pass an Apikey to a webservice as basic auth 'apiKey' => 'huda7da97hre3rhr1yrh0130409u1u' for example but I cannot work out how to implement it into the method, or even if that is the proper place for it.
I have a class called connection with my request method in it. I need to post 'apiKey' as header and not in the body. I have read the ruby docs but I cannot work out how to apply it to this specific class.
require "net/http"
require "uri"
require "ostruct"
require "json"
class Connection
ENDPOINT = "http://localhost"
APP_LOCATION = "/task_manager/v1/"
VERB_MAP = {
:get => Net::HTTP::Get,
:post => Net::HTTP::Post,
:put => Net::HTTP::Put,
:delete => Net::HTTP::Delete
}
def initialize(endpoint = ENDPOINT)
uri = URI.parse(endpoint)
#http = Net::HTTP.new(uri.host, uri.port)
end
def get(path, params)
request_json :get, path, params
end
def post(path, params)
request_json :post, APP_LOCATION + path, params
end
def put(path, params)
request_json :put, path, params
end
def delete(path, params)
request_json :delete, path, params
end
private
def request_json(method, path, params)
response = request(method, path, params)
body = JSON.parse(response.body)
OpenStruct.new(:code => response.code, :body => body)
rescue JSON::ParserError
response
end
def request(method, path, params = {})
case method
when :get
full_path = encode_path_params(path, params)
request = VERB_MAP[method.to_sym].new(full_path)
else
request = VERB_MAP[method.to_sym].new(path)
request.set_form_data(params)
end
#http.request(request)
end
def encode_path_params(path, params)
encoded = URI.encode_www_form(params)
[path, encoded].join("?")
end
end
If I post to the server using Advanced Rest Client and put the apikey in the
http://localhost/task_manager/v1/tasks?=
header
Authorization: 9c62acdda8fe12507a435345bb9b2338
and in the body
email=free%40mail.com&password=free&task=test
then I get
{
error: false
message: "Task created successfully"
task_id: 5
}
So how can I post it using this class?.
connection = Connection.new
result = connection.post("task", {'task' => 'task'})
Basic Authentication example:
req = Net::HTTP::Get.new(uri)
req.basic_auth 'user', 'pass'
http://docs.ruby-lang.org/en/trunk/Net/HTTP.html#class-Net::HTTP-label-Basic+Authentication
Or if you want to add a raw Authorization header in your request method you can do
request.add_field 'Authorization', 'huda7da97hre3rhr1yrh0130409u1u'
But basic authentication normally means that there is a user name and a password. With your API key - I am not sure you actually need basic authentication. I do not know what you API actually requires but if you have not tried it yet you can try sending the api key as an additional parameter
result = connection.post("register", {'email' => email, 'name' => name, 'password' => password, 'apiKey' => 'huda7da97hre3rhr1yrh0130409u1u' })

Devise Json Authentication

I am writing backend of an app in Rails. As I work on the backend, I need to give the frontend developer a REST API to start building the frontend. Eventually, the frontend and backend will reside together in a single app, but for now they are separate.
For time being I have enabled Cross-origin resource sharing in my app, by adding following to ApplicationController:
config.action_dispatch.default_headers.merge!({
'Access-Control-Allow-Origin' => '*',
'Access-Control-Request-Method' => '*'
});
For now, I have also turned off CSRF tokens by adding following to application.rb:
skip_before_filter :verify_authenticity_token
I am using Devise for authenticating users. To make Devise work with JSON requests, I have done following:
In devise.rb
config.navigational_formats = ['*/*', :html, :json]
In routes.rb
devise_for :users, :controllers => {:omniauth_callbacks => "omniauth_callbacks", :sessions => 'sessions', :registrations => 'registrations' }
My SessionsController
class SessionsController < Devise::SessionsController
#todo had to do following to support logging in through ajax. need to add logic to send back error response when login fails.
#todo see http://stackoverflow.com/questions/5973327/using-devise-1-3-to-authenticate-json-login-requests/8402035#8402035 and
#todo https://web.archive.org/web/20130928040249/http://jessehowarth.com/devise
#todo see http://stackoverflow.com/questions/11277300/devise-failure-authentication-via-json-sends-back-html-instead-of-json
def create
respond_to do |format|
format.html { super }
format.json {
resource = warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#failure")
sign_in(resource_name, resource)
return render :json => {:success => true, :user => resource}
}
end
end
def destroy
respond_to do |format|
format.html { super }
format.json {
Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
render :json => {}
}
end
end
def failure
render :json => {:success => false, :errors => ["Login Failed"]}, :status => 422
end
end
I have a extended Devise's RegistrationsController as well as indicated in routes.rb, but am not posting its content here, as I don't think it is relevant to this question.
With the above setup I am able to send an ajax request to '/users/sign_in' with user[email] and user[password] parameters and have the user signed in. The response looks something like this:
{
success: true
user: {
authentication_token: "SNa2kPqkm5ENsZMx7yEi"
created_at: "2014-12-16T02:40:39.179Z"
email: "xyz#xyz.com"
id: 99999
name: null
provider: null
uid: null
updated_at: "2014-12-17T02:29:31.537Z"
}
}
Now how do I use the authentication_token I received in the sign_in response to send requests to other controller actions that require user to be authenticated? Do I need to set this token in a request header? I am not able to find information on how to use this token. Please help.
It seems following as described in the gist here, the answer is that you send the suer's email and authetication_token with every request to the backend. You may choose to send it in request header or simply as parameters. You simply modify the method that checks the email and token and signs in the user in ApplicationController accordingly. This is my ApplicationController (I am now sending the email and token as parameters in the request):
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
#todo remove this once ui is integrated. following turns off the csrf token:
skip_before_filter :verify_authenticity_token
#todo begin code to support authentication using token
# This is our new function that comes before Devise's one
before_filter :authenticate_user_from_token!
# This is Devise's authentication
before_filter :authenticate_user!
private
def authenticate_user_from_token!
user_email = params[:user_email].presence
user = user_email && User.find_by_email(user_email)
# Notice how we use Devise.secure_compare to compare the token
# in the database with the token given in the params, mitigating
# timing attacks.
if user && Devise.secure_compare(user.authentication_token, params[:user_token])
sign_in user, store: false
end
end
#todo end code to support authentication using token
end
I forgot to mention in my post that I had already added the migration to add a authentication_token column to User model. Also, I had to add following in the User model (as described in the gist), so that an authentication token is generated each time a user is created/updated:
#todo begin code to support ajax authentication of users
#todo see https://gist.github.com/josevalim/fb706b1e933ef01e4fb6
# You likely have this before callback set up for the token.
before_save :ensure_authentication_token
def ensure_authentication_token
if authentication_token.blank?
self.authentication_token = generate_authentication_token
end
end
private
def generate_authentication_token
loop do
token = Devise.friendly_token
break token unless User.where(authentication_token: token).first
end
end
#todo end code to support ajax authentication of users

How to add response header in VHost or Passeneger (Ruby)

I have problem with Same Origin Policy. I want to make cross domain request - I found nice solution: http://www.w3.org/TR/cors/
But I don't want set header in Apache because I have there many domains and only one need it. Is it possible to add Access-Control-Allow-Origin header via Virtual Host or Passenger?
I do it, because I need use Redmine REST API (XHR) in Chrome/Mozilla plugin.
I had a similar requirement. If you want Redmine to serve these headers then you need to modify the Redmine source. I've written a blog post about doing this.
Credit to this blog post for most of the details.
I'll reproduce what I had to do here for convenience:
First let's adress the preflight check. I've added a whole new controller, just for this, at /app/controllers/cors_controller.rb. It looks like:
class CorsController < ApplicationController
skip_before_filter :session_expiration, :user_setup, :check_if_login_required, :set_localization
def preflight
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS, PUT'
headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-Prototype-Version, Content-Type'
headers['Access-Control-Max-Age'] = '1728000'
render :text => '', :content_type => 'text/plain'
end
end
Pretty simple stuff. I've then routed all OPTIONS requests to this controller in /config/routes.rb:
match '*path', :to => 'cors#preflight', :constraints => {:method => 'OPTIONS'}
Preflight checks taken care of, it's just a case of adding the headers to the main response using an after_filter in /app/controllers/application_controller.rb as suggested by Tom:
class ApplicationController < ActionController::Base
include Redmine::I18n
# ...
before_filter :session_expiration, :user_setup, :check_if_login_required, :set_localization
#************ Begin Added Code ****************
after_filter :cors_set_access_control_headers
# For all responses in this application, return the CORS access control headers.
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS, PUT'
headers['Access-Control-Max-Age'] = "1728000"
end
#************* End Added Code *****************
#...
end

Resources