I have a rails app on heroku that is serving up data via an API. This data looks like...
[{"created_at":"2014-02-20T17:22:02Z","id":1,"name":"Joe Rogan","twitter":"#joerogan","updated_at":"2014-02-20T17:22:02Z"},{"created_at":"2014-02-20T17:22:11Z","id":2,"name":"Kristen Schaa","twitter":"#kchalithis","updated_at":"2014-02-20T17:22:11Z"},{"created_at":"2014-02-20T17:29:10Z","id":3,"name":"Casey Grim","twitter":"#aCoupleofN3rds","updated_at":"2014-02-20T17:29:16Z"}]
I pulled down the Ember Start Kit and have modified it to pull data from my API with code like this...
js/app.js
App.Person.adapter = Ember.RESTAdapter.create();
App.Person.url = "http://myapp.herokuapp.com/api/v1/shots?api_key=12d2d06fb2f6a786ac75b32625cf83a1";
App.Person.collectionKey = "people";
index.html
<script type="text/x-handlebars" id="index">
<ul>
{{#each item in model}}
<li>{{item.name}}</li>
{{/each}}
</ul>
</script>
I thought I could start chrome like this...
google-chrome --disable-web-security
And get it working just for testing, but that does not work.
Also, Sounds like I need to use jsonp rather than json? But not sure how to implement in ember (or really anywhere else). Any help appreciated. Thanx!
Update
Added :cors_set_access_control_headers before filter in my rails app. looks like...
module Api
module V1
class ShotsController < ApplicationController
before_filter :cors_set_access_control_headers
...
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
headers['Access-Control-Request-Method'] = '*'
headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
end
...but still getting error
XMLHttpRequest cannot /api/v1/shots?api_key=d26da3938adc5f3c8604256194c18501.json. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
...if I issue a curl to look at headers I'm seeing Access Controll set to *...
me#me-E6530:~$ curl -v http://myapp.herokuapp.com/api/v1/shots?api_key=12d2d06fb2f6a786ac75b32625cf83a1
* About to connect() to myapp.herokuapp.com port 80 (#0)
* Trying 54.243.169.60... connected
> GET /api/v1/shots?api_key=12d2d06fb2f6a786ac75b32625cf83a1 HTTP/1.1
> User-Agent: curl/7.22.0 (i686-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: myapp.herokuapp.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization
< Access-Control-Allow-Methods: POST, PUT, DELETE, GET, OPTIONS
< Access-Control-Allow-Origin: *
< Access-Control-Request-Method: *
Ember expects response in the following format:
{
people: [
{
id: 1,
name: "John Doe"
},
{
id: 2,
name: "Jane Doe"
}
]
}
Also you may need to enable CORS if they are on different domains.
Related
I am sending the request to register a user from my Next.js frontend like this:
const res = await fetch("http://localhost:3000/create", {
method: "POST",
headers: {
"Contet-Type": "application/json",
},
body: JSON.stringify({ user: {values}),
});
const data = await res.json();
where the values are username, email, pw, pw confirmation...the usual.
Here are my headers from the request, which confirm that the payload is being sent as expected:
General:
Request URL: http://localhost:3000/create
Request Method: POST
Status Code: 204 No Content
Remote Address: 127.0.0.1:3000
Referrer Policy: strict-origin-when-cross-origin
Response headers:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD
Access-Control-Allow-Origin: http://localhost:3001
Access-Control-Max-Age: 1728000
Cache-Control: no-cache
Vary: Origin
X-Request-Id: a044ad4b-dbb3-41ed-80bd-ca933871b924
X-Runtime: 115.547357
Request headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en,nl;q=0.9,hr;q=0.8,en-GB;q=0.7,en-US;q=0.6
Connection: keep-alive
Content-Length: 27
Content-Type: text/plain;charset=UTF-8
Contet-Type: application/json
Host: localhost:3000
Origin: http://localhost:3001
Referer: http://localhost:3001/
sec-ch-ua: "Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"
sec-ch-ua-mobile: ?0
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36
Request payload:
{user:{email:"ggggg#gmail.com", username:"myusername", password:"123123",password_confirmation:"123123"}}
But what happens is the parameters from the frontend request never show up in my controllers.
So naturally, my first step was to byebug my rails controller in order to figure out what is going on, and when I do that all I see from params is:
<ActionController::Parameters {"controller"=>"user", "action"=>"create"} permitted: false>
My user params are nowhere to be seen. What is going on here?
I even started another rails project through a template:
https://github.com/rchuasing/rails-api-boilerplate
and the same thing keeps happening again, I am recieving no parameters in any controller.
Here is my controller that I've stripped down to bare minimum in order to just test the params:
class UserController < ApplicationController
def create
# this is where I look for params
byebug
end
def user_params
params.require(:user).permit(:email, :username, :password,
:password_confirmation)
end
end
Model:
class User < ApplicationRecord
include Devise::JWT::RevocationStrategies::JTIMatcher
devise :database_authenticatable, :registerable, :jwt_authenticatable,
jwt_revocation_strategy: self
end
I have tried everything I could think of, that includes making a new app and trying all kinds of various headers, but the params never get to the backend.
I am trying to implement devise/oauth to azure AD with steps from this article.
https://medium.com/committed-engineers/setup-azure-ad-oauth-2-0-with-ruby-on-rails-and-devise-39848e3ed532
It looks that gem is working, I get link to login.microsoft.com with params, which is sent by post method, but I get CORS error
Access to fetch at
'https://login.microsoftonline.com/xxxxxx/oauth2/v2.0/authorize?client_id=zzzz&prompt&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fusers%2Fauth%2Fazure_activedirectory_v2%2Fcallback&response_type=code&scope=openid+profile+email&state=xxxxx'
(redirected from
'http://localhost:3000/users/auth/azure_activedirectory_v2') from
origin 'http://localhost:3000' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. If an opaque response serves your needs, set the request's
mode to 'no-cors' to fetch the resource with CORS disabled.
When I check network in console, I see that headers looks fine
> Request URL: http://localhost:3000/users/auth/azure_activedirectory_v2
> Request Method: POST Status Code: 302 Found Remote Address: [::1]:3000
> Referrer Policy: strict-origin-when-cross-origin
> Access-Control-Allow-Credentials: true Access-Control-Allow-Methods:
> GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS
> Access-Control-Allow-Origin: http://localhost:3000
> Access-Control-Expose-Headers Access-Control-Max-Age: 7200
> Cache-Control: no-cache Content-Length: 361 Location:
> https://login.microsoftonline.com/xxxx
I use rack-cors gem with this config in cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'http://localhost:3000'
resource '*',
headers: :any,
methods: :any,
credentials: true
end
end
Rails.application.config.hosts << ".microsoft.com"
What am I missing? When I manually open location of that post request I see in console, I get login screen for microsoft account what is expected
edit: When I enable debug in cors.rb, I see that just to my post request there is no header
Incoming Headers:
Origin: http://localhost:3000
Path-Info: /mini-profiler-resources/results
Access-Control-Request-Method:
Access-Control-Request-Headers:
{"Access-Control-Allow-Origin"=>"http://localhost:3000", "Access-Control-Allow-Methods"=>"GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS", "Access-Control-Expose-Headers"=>"", "Access-Control-Max-Age"=>"7200", "Access-Control-Allow-Credentials"=>"true"}
Incoming Headers:
Origin: http://localhost:3000
Path-Info: /mini-profiler-resources/results
Access-Control-Request-Method:
Access-Control-Request-Headers:
{"Access-Control-Allow-Origin"=>"http://localhost:3000", "Access-Control-Allow-Methods"=>"GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS", "Access-Control-Expose-Headers"=>"", "Access-Control-Max-Age"=>"7200", "Access-Control-Allow-Credentials"=>"true"}
Incoming Headers:
Origin: http://localhost:3000
Path-Info: /mini-profiler-resources/results
Access-Control-Request-Method:
Access-Control-Request-Headers:
{"Access-Control-Allow-Origin"=>"http://localhost:3000", "Access-Control-Allow-Methods"=>"GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS", "Access-Control-Expose-Headers"=>"", "Access-Control-Max-Age"=>"7200", "Access-Control-Allow-Credentials"=>"true"}
Incoming Headers:
Origin: http://localhost:3000
Path-Info: /users/auth/azure_activedirectory_v2
Access-Control-Request-Method:
Access-Control-Request-Headers:
ok for everybody who will spend lot of time trying to find a problem
https://accidentaltechnologist.com/ruby-on-rails/hotwire-fix-for-cors-error-when-using-omniauth/
Actually, only thing needs to be done is change link_to to button_to helper and disable turbo for it, so add data-turbo false
this is how it should look and all works well, I can authenticate with AD
<%= button_to "Log in with Azure AD", user_azure_activedirectory_v2_omniauth_authorize_path, method: :post, data: {turbo: "false"}, class: "btn btn-primary" %>
The short solution is to add data: {turbo: "false"} to the link to trigger the OAuth request.
I'm working on a rails API that is meant to work with a VUE frontend. This is my first time working with rails as an API only.
I'm familiar with using devise for user authentication in other rails apps.
Our sign in works, same with logout. I can't figure out how to edit and delete users though.
My rake routes shows this:
new_api_user_session GET /api/v1/auth/sign_in(.:format) devise_token_auth/sessions#new
api_user_session POST /api/v1/auth/sign_in(.:format) devise_token_auth/sessions#create
destroy_api_user_session DELETE /api/v1/auth/sign_out(.:format) devise_token_auth/sessions#destroy
new_api_user_password GET /api/v1/auth/password/new(.:format) devise_token_auth/passwords#new
edit_api_user_password GET /api/v1/auth/password/edit(.:format) devise_token_auth/passwords#edit
api_user_password PATCH /api/v1/auth/password(.:format) devise_token_auth/passwords#update
cancel_api_user_registration GET /api/v1/auth/cancel(.:format) devise_token_auth/registrations#cancel
new_api_user_registration GET /api/v1/auth/sign_up(.:format) devise_token_auth/registrations#new
edit_api_user_registration GET /api/v1/auth/edit(.:format) devise_token_auth/registrations#edit
api_user_registration PATCH /api/v1/auth(.:format) devise_token_auth/registrations#update
My routes.rb is like so:
Rails.application.routes.draw do
root 'home#index'
namespace :api do
scope :v1 do
resource :profile, only: [:show]
mount_devise_token_auth_for "User", at: 'auth'
## other code
end
end
end
I'm testing the api using CURL. The following command can successfully log in:
curl -X POST -v -H 'Content-Type: application/json' http://localhost:3000/api/v1/auth/sign_in -d '{"email": "test#example.com", "password": "password"}'
That works OK.
The route for updating a user should be http://localhost:3000/api/v1/auth/
The sign in gives me the client and access-token values. When I pop those into the following curl command I expect the user to update. The attribute that I'm trying to update is "sortOrder".
curl -X PATCH -v -H 'Content-Type: application/json' -H 'access-token: XUbhcAgWlVnARf9Ps4rjR' -H 'client: Lxg5us7MpUyAuM5xQGNuqg' -H 'uid: test#example.com' http://localhost:3000/api/v1/auth/ -d '{"sortOrder": "DESC"}'
From that command, my rails log shows:
Started PATCH "/api/v1/auth/" for 127.0.0.1 at 2018-12-26 12:10:53 -0800
Processing by DeviseTokenAuth::RegistrationsController#update as */*
Parameters: {"sortOrder"=>"DESC", "registration"=>{"sortOrder"=>"DESC"}}
User Load (0.6ms) SELECT `users`.* FROM `users` WHERE `users`.`uid` = 'test#example.com' LIMIT 1
Unpermitted parameter: registration
Completed 404 Not Found in 101ms (Views: 0.3ms | ActiveRecord: 0.6ms)
The output from the curl command is:
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 3000 (#0)
> PATCH /api/v1/auth/ HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.47.0
> Accept: */*
> Content-Type: application/json
> access-token: XUbhcAgWlVnARf9Ps4rjR
> client: Lxg5us7MpUyAuM5xQGNuqg
> uid: test#example.com
> Content-Length: 50
>
* upload completely sent off: 50 out of 50 bytes
< HTTP/1.1 404 Not Found
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< Content-Type: application/json; charset=utf-8
< Cache-Control: no-cache
< X-Request-Id: 1e5e61f6-1226-43dc-b8b3-30a576b0c03a
< X-Runtime: 0.104965
< Vary: Origin
< Transfer-Encoding: chunked
<
* Connection #0 to host localhost left intact
{"success":false,"errors":["User not found."],"status":"error"}
I'm not sure what I'm missing here. With this devise set up I don't have access to the devise/registrations controller. I could probably create my own but this should work. I haven't been able to find documentation for the devise_token_auth gem that addresses user updates and delete.
The result that I'm getting seems to be "user not found". I've tried several ways to get the user UID, email and id in the request but nothing seems to work. Has anyone experienced this before?
Looks like the user does not get saved somehow. I would try adding :create to the resource :profile, only: [:show, :create] in routes.rb. I am just learning RoR an Devise so I can't say that it is a 100% cure but worth a try. Following the same logic, you can add the :edit and :destroy actions to edit/delete profile/user.
I have a straightforward Rails 4.1.4 application and I'm trying to connect an AngularJS application on a separate host to it's API. Whilst I'm have no problem accessing it, Rails seems to think the request is HTML and ignores the Content-Type: 'application/json'
Started GET "/api/v1/locations?access_token=xxx&headers=%7B%22Content-type%22:%22application%2Fjson%22%7D" for 127.0.0.1 at 2014-09-03 17:12:11 +0100
Processing by Api::V1::LocationsController#index as HTML
Parameters: {"access_token"=>"xxx", "headers"=>"{\"Content-type\":\"application/json\"}"}
And in my NG application, I've tried a number of combinations of headers including:
app.factory('Location', ['$resource', "$localStorage",
function($resource, $localStorage){
return $resource('http://my-api.com/api/v1/locations/', {headers:{'Content-type' : 'application/json'}}, {
query: {
method: 'GET',
headers: {'Content-type': 'application/json'},
isArray: true,
dataType: 'json',
params: { access_token: $localStorage.accessToken }
}...
The response looks ok on the NG side, it's responding with JSON despite only having this in my locations controller:
class Api::V1::LocationsController < Api::V1::BaseController
doorkeeper_for :all
before_filter :authorize
respond_to :json
def index
#locations = #current_user.locations.order("created_at desc").limit(5)
respond_with #locations
end
end
I have also set (and tested unset) the cors headers.
I read somewhere that Rails won't read the content-type header if there's forward slashes in it...
Whilst this doesn't appear to be causing many issues, I do think it's interfering with Doorkeeper that's part of the application.
This wasn't a Rails problem. Turns out I needed to fiddle with some headers etc. in the NG config.
I added the following to app.js
$httpProvider.defaults.useXDomain = true;
// $httpProvider.defaults.withCredentials = true;
delete $httpProvider.defaults.headers.common["X-Requested-With"];
$httpProvider.defaults.headers.common["Accept"] = "application/json";
$httpProvider.defaults.headers.common["Content-Type"] = "application/json";
The second line threw an error but I've left in there for good measure.
I'm using Private_Pub gem which is built on Faye, and I'm using a another app for the Faye which is provided by this blog the project that I found. to get Faye running on an app and my actual site on another app.
The error in FireFox console:
The connection to ws://xxxxxxxxxxx.herokuapp.com/faye was interrupted while the page was loading. Faye.js
and in Chrome:
WebSocket connection to 'ws://xxxxxxxxxxx.herokuapp.com/faye' failed: One or more reserved bits are on: reserved1 = 1, reserved2 = 0, reserved3 = 0
on the Chat (Faye) app log I get this in the log :
at=error code=H12 desc="Request timeout" method=GET path=/faye host=xxxxxxxxxxx.herokuapp.com request_id=xxxxxxxxxxx fwd="xxxxxxxxxxx" dyno=web.1 connect=31ms service=30112ms status=503 bytes=0
any suggestions: ?
I have added an after_filter in the application_controller as well to allow the domain request
This was an issue with my application also, heroku has a timeout of 30 and if the faye.ru/config.ru has timeout set more than 30 this error would come. try reducing it to 25 or something.
faye_server = Faye::RackAdapter.new(:mount => '/faye', :timeout => 25)
To solve this problem, the Rails Apps need to let each other access or let's say ping each other. In order to do that, we have to use something like CORS, to allow AJAX Request from Other Domain.
Here is a simpler way that I used to go allow method accesses article, it might sound confusing at first.
To do perform this on both sides, you need to create an actual complete app for the Faye as well, unlike what is given on the Faye chat server Repo I've attached in my question.
Step 1 :
In the Actual/Main App include this in your application_controller.rb
before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers
#For all responses in this controller, return the CORS access control headers.
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = 'http://YOURFAYEAPP.com'
headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
headers['Access-Control-Request-Method'] = 'http://YOURFAYEAPP.com'
headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
end
# If this is a preflight OPTIONS request, then short-circuit the
# request, return only the necessary headers and return an empty
# text/plain.
def cors_preflight_check
if request.method == :options
headers['Access-Control-Allow-Origin'] = 'http://YOURFAYEAPP.com'
headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
headers['Access-Control-Request-Method'] = 'http://YOURFAYEAPP.com'
headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
render :text => '', :content_type => 'text/plain'
end
end
Step 2:
on the Faye app side include the same thing in the application_controller.rb, but change the domain name to the Original/Actual/Main Rails app.
In that way, both can send and receive requests.
Don't put * instead of the request parameter, it's a security issue. You can cut down the methods to only Get and Post.