How can I add custom header parameters to a API GET call in clojurescript - api

I'm building a demo application in clojurescript with KeeFrame and to retrieve a part of the information for this website I need to call an external API which requires a custom HTTP header parameter in the GET requests
I'm using re-frame.core for the API calls, which uses ajax.core.
I also tried to replace this with cljs-http.client. However the result is the same.
I already managed to add custom header parameters to the request header by using clj-http at server site. But this is not a solution I want to implement for this website because that means that I first have to rebuild the API I'm calling. So I can use it from my clojurescript without the parameter.
This code works. A correct GET request is generated
{:http-xhrio {
:method :get
:uri (str transuri "/acquirer/" 673072009 "/acquirerref/" acquirerRefNo)
:headers {"Accept" "application/json"}
:response-format (http/json-response-format {:keywords? true})
:on-failure [:common/set-error]}}
With "Accept: application/json" as a request header
This code does not work. Instead of a GET request an OPTIONS request is generated
{:http-xhrio {
:method :get
:uri (str transuri "/acquirer/" 673072009 "/acquirerref/" acquirerRefNo)
:headers {"Accept" "application/json" "Custom" "Value"}
:response-format (http/json-response-format {:keywords? true})
:on-failure [:common/set-error]}}
And in the request header "Accept: application/json" is not visible but "Access-Control-Request-Headers: custom" is
I expected a GET request with "Accept: application/json" and "Custom: Value" in the request header.
Can someone tell me what I'm doing wrong or provide me with a link with information about this?
Thanks in advance

The Browser will send a "preflight" OPTIONS request to verify that it is allowed to send the "Custom" request header. The server is supposed to approve by replying with "Access-Control-Allow-Headers".
See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

I have not used KeeFrame, but I have a working example using a new re-frame lib I'm working on. It invokes an ajax request using this interceptor:
(def ajax-intc
"Interceptor for performing AJAX requests"
(interceptor
{:id :ajax-intc
:enter identity
:leave (fn [ctx] ; #todo (with-result ctx ...)
(let [ajax (:ajax ctx)]
;(t/spyx :ajax-intc-start ctx)
;(t/spyx :ajax-intc-start ajax)
(when-not (nil? ajax)
(t/spy :awt-ajax-intc--ajax ajax)
(let [method (t/grab :method ajax)
uri (t/grab :uri ajax)
ajax-opts-present (set/intersection (set (keys ajax)) ajax-options-keys)
opts-map (t/submap-by-keys ajax ajax-opts-present)]
;(t/spy :ajax-intc-ready (t/vals->map method uri opts-map))
(condp = method
:get (do
(t/spy :awt-ajax-intc--opts-map opts-map)
(ajax/GET uri opts-map))
:put (ajax/PUT uri opts-map)
:post (ajax/POST uri opts-map)
(throw (ex-info "ajax-intc: unrecognized :method" ajax))))))
ctx)}))
When invoked with this event:
(flame/dispatch-event [:ajax-demo :get "/fox.txt"
{:handler ajax-handler
:error-handler ajax-error-handler
:headers {"custom" "something"}
}])
one can see in the Chrome dev console that the headers come through:
:awt-localstore-load-intc--loaded-value-1 {}
core.cljs:192 :awt-ajax-intc--ajax => {:method :get, :uri "/fox.txt", :handler #object[flintstones$core$ajax_handler], :error-handler #object[flintstones$core$ajax_error_handler], :headers {"custom" "something"}}
core.cljs:192 :awt-ajax-intc--opts-map => {:handler #object[flintstones$core$ajax_handler], :error-handler #object[flintstones$core$ajax_error_handler], :headers {"custom" "something"}}
If you want to try it out, you can clone this repo: git#github.com:cloojure/cljs-enflame.git
and then run:
lein clean
lein figwheel
and see it run in the browser.

Related

Clojure httpkit spotify request

Following my last question, but to keep the code homogeneous I want to use httpkit to make requests instead of clj-http as I did before.
I have the following to try and receive my profile from the Spotify API:
(defn get-me [token]
#(client/request
{:url "https://api.spotify.com/v1/me"
:method :get
:headers {"Authorization" token
"Accept" "application/json"
"Content-Type" "application/json"}}))
(defn get-me-handler [req]
(let [res (:token #TOKEN)]
(if (= res "")
{:status 404
:headers {"Content-Type" "text/html"}
:body "Not logged in"}
{:status 200
:headers {"Content-Type" "text/html"}
:body (get-me (get (json/read-str res) "access_token"))})))
a long with a "localhost:{port}/me" route. When I access this route, I get back:
{"status": 400, "message": "Only valid bearer authentication supported" }}"
As the error message. Does anyone know how to fix this in httpkit, as it is not a problem I had in clj-http?

How do I add CORS to a compojure-api app?

How can I add CORS to this code snippet?
(def app
(api
{:swagger {:ui "/docs"
:spec "/swagger.json"}}
(GET "/route-a" [] "a")
(GET "/route-b" [] "b")
(GET "/route-c" [] "c")))
I would like to use https://github.com/r0man/ring-cors and have tried this, but it did not seem to do anything. I would like to see the response header contain Access-Control-Allow-Origin but it is missing.
(-> (api
{:swagger {:ui "/docs"
:spec "/swagger.json"}}
(GET "/route-a" [] "a")
(GET "/route-b" [] "b")
(GET "/route-c" [] "c"))
(wrap-cors :access-control-allow-origin #"http://localhost:81"
:access-control-allow-headers ["Origin" "X-Requested-With"
"Content-Type" "Accept"]
:access-control-allow-methods [:get :put :post :delete :options]))
The CORS-specific response headers are returned only if the request has an Origin header that matches the specified regex (when a request is made using XMLHttpRequest in browser, the Origin header is added automatically).
If you try:
curl -vH "Origin: http://localhost:81" localhost:3000/route-a
(assuming that your API is available on port 3000), you will see that the necessary response headers are added. AJAX requests from http://localhost:81 should also work.

How do I properly set the request header of a Rhomobile API call?

Environment:
Windows Server 2012 Standard
rhodes-3.5.1.13 - MotorolaRhoMobileSuite2.2.1.13
jQuery Mobile 1.6.4
Java jdk1.6.0_24
Description:
After making this call..
Rho::AsyncHttp.get(
:url => url,
:headers => {
'Accept'=>'application/json',
'Accept-Encoding'=>'gzip,deflate,sdch',
'Accept-Language'=>'en-US,en;q=0.8'
},
:callback => callback,
:callback_param => callback_param
)
...the result of this request returns the following:
"Rho::AsyncHttp....callback_param)" {"rho_callback"=>"1", "status"=>"error", "error_code"=>"1", "headers"=>{"Accept"=>"application/json", "Accept-Encoding"=>"gzip,deflate,sdch", "Accept-Language"=>"en-US,en;q=0.8", "User-Agent"=>"Mozilla-5.0 (ANDROID; Win32; 4.0.3)"}, "body"=>""}
Question:
Is there a Rhomobile limitation on the headers that I am allowed to pass to this call? It seems to work if I paste the url, with the headers, into Fiddler. When I remove 'Accept-Encoding'=>'gzip,deflate,sdch', the response is the same.
Thanks in advance.
Rhodes uses libcURL for implementing the AsyncHTTP API in v3.5 of the framework as is possible to see in the sourcecode.
So whatever is possible to do in libcURL, the same is possible to do in ASyncHTTP.
In your particular case I'll probably use the "Content-Type" instead of the "Accept" header:
Rho::AsyncHttp.get(
:url => url,
:headers => {
'Content-Type'=>'application/json',
'Accept-Encoding'=>'gzip,deflate,sdch',
'Accept-Language'=>'en-US,en;q=0.8'
},
:callback => callback,
:callback_param => callback_param
)
If you want to test this in Rhodes v4.0, there now the Network API that you can use in the same way:
getProps = Hash.new
getProps['url'] = url
getProps['headers'] = {"Content-Type" => "application/json",
"Accept-Encoding"=>"gzip,deflate,sdch",
"Accept-Language"=>"en-US,en;q=0.8"}
Rho::Network.get(getProps, url_for(:action => :callback))

rails - "WARNING: Can't verify CSRF token authenticity" for json devise requests

How can I retrieve the CSRF token to pass with a JSON request?
I know that for security reasons Rails is checking the CSRF token on all the request types (including JSON/XML).
I could put in my controller skip_before_filter :verify_authenticity_token, but I would lose the CRSF protection (not advisable :-) ).
This similar (still not accepted) answer suggests to
Retrieve the token with <%= form_authenticity_token %>
The question is how? Do I need to do a first call to any of my pages to retrieve the token and then do my real authentication with Devise? Or it is an information one-off that I can get from my server and then use consistently (until I manually change it on the server itself)?
EDIT:
In Rails 4 I now use what #genkilabs suggests in the comment below:
protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }
Which, instead of completely turning off the built in security, kills off any session that might exist when something hits the server without the CSRF token.
skip_before_filter :verify_authenticity_token, :if => Proc.new { |c| c.request.format == 'application/json' }
This would turn off the CSRF check for json posts/puts that have properly been marked as such.
For example, in iOS setting the following to your NSURLRequest where "parameters" are your parameters:
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json"
forHTTPHeaderField:#"content-type"];
[request setValue:#"application/json"
forHTTPHeaderField:#"accept"];
[request setHTTPBody:[NSData dataWithBytes:[parameters UTF8String]
length:[parameters length]]];
You can send the CSRF token, after a successful log-in, using a custom header.
E.g, put this in your sessions#create :
response.headers['X-CSRF-Token'] = form_authenticity_token
Sample log-in response header providing the CSRF token:
HTTP/1.1 200 OK
Cache-Control: max-age=0, private, must-revalidate
Connection: Keep-Alive
Content-Length: 35
Content-Type: application/json; charset=utf-8
Date: Mon, 22 Oct 2012 11:39:04 GMT
Etag: "9d719d3b9aabd413c3603e04e8a3933d"
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-10-12)
Set-Cookie: [cut for readability]
X-Csrf-Token: PbtMPfrszxH6QfRcWJCCyRo7BlxJUPU7HqC2uz2tKGw=
X-Request-Id: 178746992d7aca928c876818fcdd4c96
X-Runtime: 0.169792
X-Ua-Compatible: IE=Edge
This Token is valid until you log-in again or (log-out if you support this through your API).
Your client can extract and store the token from the log-in response headers. Then, each POST/PUT/DELETE request must set the X-CSRF-Token header with the value received at the log-in time.
Sample POST headers with the CSRF token:
POST /api/report HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate, compress
Content-Type: application/json; charset=utf-8
Cookie: [cut for readability]
Host: localhost:3000
User-Agent: HTTPie/0.3.0
X-CSRF-Token: PbtMPfrszxH6QfRcWJCCyRo7BlxJUPU7HqC2uz2tKGw=
Documentation: form_authenticity_token
Indeed simplest way. Don't bother with changing the headers.
Make sure you have:
<%= csrf_meta_tag %>
in your layouts/application.html.erb
Just do a hidden input field like so:
<input name="authenticity_token"
type="hidden"
value="<%= form_authenticity_token %>"/>
Or if you want a jquery ajax post:
$.ajax({
type: 'POST',
url: "<%= someregistration_path %>",
data: { "firstname": "text_data_1", "last_name": "text_data2", "authenticity_token": "<%= form_authenticity_token %>" },
error: function( xhr ){
alert("ERROR ON SUBMIT");
},
success: function( data ){
//data response can contain what we want here...
console.log("SUCCESS, data="+data);
}
});
Basically when you post your json data just add a valid authenticity_token field to the post data and the warning should go away...
I resolved that error this way:
class ApplicationController < ActionController::Base
protect_from_forgery
skip_before_action :verify_authenticity_token, if: :json_request?
protected
def json_request?
request.format.json?
end
end
Source:
http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html
What's worrying is that in Rails 3.2.3 we now get the CSRF warning in production.log but the post does not fail! I want it to fail as it protects me from attacks. And you can add the csrf token with jquery before filter btw:
http://jasoncodes.com/posts/rails-csrf-vulnerability
I have used the below. Using include? so if the content type is application/json;charset=utf-8 then it is still working.
protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format.include? 'application/json' }
This answer is better.
You get to keep the CSRF-TOKEN validation with no extra effort (the token is appended) before any XMLHttpRequest send. No JQuery, no nothing just copy/paste and refresh.
Simply add this code.
(function() {
var send = XMLHttpRequest.prototype.send,
token = $('meta[name=csrf-token]').attr('content');
XMLHttpRequest.prototype.send = function(data) {
this.setRequestHeader('X-CSRF-Token', token);
return send.apply(this, arguments);
};
}());
I had the same issue with the following version of Rails:
gem 'rails', :git => 'git://github.com/rails/rails.git', :branch => '3-2-stable'
I updated to 3.2.2 and everything works fine for me now. :)
gem 'rails', '3.2.2'
I ran into the same issue tonight.
The reason that happens is because when you sign in the last csrf-token is no longer valid.
What I did was:
$("meta[name=csrf-token]").attr('content', '<%= form_authenticity_token %>'); in your app/views/devise/sessions/create.js.rb.
Now it does have a valid csrf-token :)
I hope it helps
Also for development/test mode.
protect_from_forgery with: :exception unless %w(development test).include? Rails.env
This warning shows because you are using :null_session, in Rails 4.1 it works by default if no with: options specified.
protect_from_forgery

Testing authenticated file uploads in merb

This is something that has been driving me mad over the past few days. I have an action which allows authenticated users to upload assets to the site. I know that the controller action is correct as I can run through the process manually however I want to test it using rspec.
I have to use the request helper so I can reuse an authenticated session which is a :given for this set of tests.
it "should allow authenticated file uploads" do
file = File.open(a_valid_file)
mock_file = mock("file")
mock_file.stub!(:path).and_return(file.path)
request( resource(:assets), :method => "POST",
:params => { :file =>
{:tempfile => mock_file, :filename => File.basename(file.path)} }
)
end
If I breakpoint inside the spec it all works nicely, however when I run the spec and try to access the path in the controller action through the debugger I get this:
e file[:tempfile].path
NoMethodError Exception: undefined method `path' for "#[Spec::Mocks::Mock:0x3fda2a4736c0 #name=\"file\"]":String
My guess is that the stub!(:path) is not being set for whatever mock object is making it through the request.
The question is: Am I going about the right way for testing file uploads and if not what is another way?
I was doing it wrong. By using request it was calling to_s on all paramaters, so my mock object was being passed as "#[Spec::Mocks::Mock:0x3fda2a4736c0 #name=\"file\"]". That will teach me to pay more attention to exception output.
Instead I should use multipart_post and stub out the authentication calls in a block.
it "should allow authenticated file uploads" do
file = File.open(a_valid_file)
multipart_post( resource(:assets), :method => "POST",
:params => { :file => file } ) do |controller|
controller.stub!(:ensure_authenticated).and_return(true)
controller.session.stub!(:user).and_return(User.first)
)
end