Using ajaxSetup beforeSend for Basic Auth is breaking SignalR connection - ajax

I have a WebApi secured with Basic Auth which is applied to the entire Api using a AuthorizationFilterAttribute. I also have SignalR Hubs sitting on several of my Api Controllers.
Alongside this I have a web page which makes use of my WebApi. The web page is mostly written in Backbone, so in order to make calls to my secured WebApi, I have added the following jquery
$.ajaxSetup({
beforeSend: function (jqXHR, settings) {
jqXHR.setRequestHeader('Authorization', 'Basic ' + Token);
return true;
}
});
This works for communicating with my Api Controllers, but adding the above code has broken the connection to my SignalR Hubs, specifically:
XMLHttpRequest cannot load http://localhost:50000/signalr/negotiate?_=1366795855194.
Request header field Authorization is not allowed by Access-Control-Allow-Headers.
Removing the jqXHR.setRequestHeader() line restores my SignalR Hub connection but breaks the Api calls.
Given the above, I could do something hacky and only set the request header if the request being made isn't to /signalr but that just feels dirty...
Is there a cleaner way around this?
Am I just doing something silly? Has anyone else ran in to this?

What I didn't mention before is that I have a DelegatingHandler which sends back the correct Headers to any request coming in to my WebApi. This works perfectly for any requests to my WebApi but I wrongly assumed that this would also apply to SignalR requests.
As SignalR relies on several different transport methods, it doesn't seem reasonable to assume I have access to Authorization headers in the first place - they're not a requirement of all WebSockets implementations for example (see here)
My current solution has been to make use SignalR's HubPipeline (detailed here). Using this, I believe I can pass the Basic Auth credentials in a query string and write a separate module for handling Authorization for the SignalR requests:
Passing the Query string
$.connection.hub.qs = "auth=" + MyBase64EncodedAuthString;
The Filter
public class SignalrBasicAuthFilterAttribute: Attribute, IAuthorizeHubConnection {
public bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request) {
var authString = request.QueryString["auth"];
// ... parse, authorize, etc ...
return true;
}
}
Registering the Filter
var globalAuthorizer = new SignalrBasicAuthFilterAttribute();
GlobalHost.HubPipeline.AddModule(new AuthorizeModule(globalAuthorizer, globalAuthorizer));
Additionally...
Note that because it's not a reliable assumption to send an Authorization header with SignalR requests, for the aforementioned reasons, I am still filtering my $.ajaxSetup to only affect non-SignalR requests:
$.ajaxSetup({
beforeSend: function (jqXHR, settings) {
if (settings.url.indexOf("/signalr") == -1)
jqXHR.setRequestHeader('Authorization', 'Basic ' + Token);
return true;
}
});
In doing this, I'm leaving SignalrBasicAuthFilterAttribute class to take on full responsibility for Authorizing SignalR requests.
Further Reading:
http://weblogs.asp.net/davidfowler/archive/2012/11/11/microsoft-asp-net-signalr.aspx
http://eworldproblems.mbaynton.com/2012/12/signalr-hub-authorization/
SignalR authentication within a Controller?

I think the real solution for the issue will be to make sure that "Authorization" is part of the allowed Headers(Access-Control-Allow-Headers) returned from the signalR response for "negotiate" request.
You could register the header in your web.config just like this possibility.
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Headers" value="Authorization" />
</customHeaders>
</httpProtocol>

Related

adding security http headers to aws lambda function

We have a simple application structure that our ReactJs front-end make request to api gateway which does a proxy-integration with a lambda function. Since our api gateway is passing requests as they are without any modification and do the same when returning responses to customer so the place we are going to add http security headers would be in the lambda function itself. I have done some research on how it can be achived but all the answers I got searching in Google mention lambda#Edge+Cloudfront similar to this post which we do not use at all, does it mean we have to change our structure by adding these two things? Thanks.
The article you reference assumes the backend is static (e.g. S3) and cannot set headers. That's why Lambda#Edge is used.
It sounds like your current setup should work without any changes... Did you try adding headers in the code?
I have this code working perfectly for the APIGW + Lambda (proxy integration) combo.
exports.handler = async function (event) {
var response = {
statusCode: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8',
'X-My-Header': 'whatever'
},
body: JSON.stringify({status: 'OK'}),
}
return response
}
Add HSTS header in AWS Lambda.

Providing auth header with SockJS

I have a Spring MVC server that provides a bunch of REST endpoints as well as a websocket endpoint. Everything except the login endpoint requires authentication. I'm using JWT to authenticate requests coming from the client.
When the user logs in I'm returning an X-AUTH-TOKEN header, containing the JWT token. This token is then passed in the same header on every request to the server. This all works fine for the REST endpoints, but I can't figure out how to do this on the websocket.
I'm using SockJS, and when I open the connection:
var socket = new SockJS('/socket/updates', null, {});
This causes a GET request to /socket/updates/info?t=xxx which returns a 403 (as everything requires auth by default).
Ideally I'd simply send my X-AUTH-TOKEN header on any XHR requests SockJS makes, but I can't see any way of adding headers looking at the API.
Worst case I can change SockJS to do this, but I'm wondering whether this functionality has been deliberately left out? I know SockJS doesn't support cookies for security reasons but that's not what I'm trying to do.
Also, for the sake of a test I did allow the info endpoint to have anonymous access but then it 403's on a bunch of other endpoints - it feels more elegant to simply pass in auth details on these requests than poke holes in my server security.
Any help appreciated.
Cheers
You cannot set the header from SockJS. Not because SockJS does not have this functionality, but because browser makers don't expose this API to Javascript. See:
https://github.com/sockjs/sockjs-client/issues/196
For a workaround, see JSON Web Token (JWT) with Spring based SockJS / STOMP Web Socket.
client side:
stompClient.connect({headername:header}, function () {
setConnected(true);
stompClient.subscribe(request.topic, function (message) {
output(message.body);
});
});
server side :
StompHeaderAccessor accessor
= MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
String headervalue= accessor.getNativeHeader("your header name").get(0);

NodeJS|SailsJS|PassportJS AJAX Authentication: Making Successive Requests for Data

Making Successive Requests for Data
TL;DR
After authentication, I cannot request data from my app's Front-End -- but only through server-side views and Postman can I make subsequent requests for data after logging in, or by authenticating my user in Postman and then making the data request in my app.
First off, I'm a newbie on the server-side.
I've a SailsJS backend which I'm using for REST. Creating and authenticating a user, using LocalStrategy, works fine -- and really, even making subsequent requests for data works fine -- but not via AJAX from my app.
I can use Postman or server-side views to access data, such as /list; making requests after authentication in my app doesn't work -- UNLESS I jump back into Postman and login, then jump back to my app and remake the request.
I do notice that my set-cookie's in my app are different between the first authentication request and the request for /list.
If necessary, I can show some code, but this seems I'm missing a very high-level, basic concept in making authenticated AJAX requests.
EDIT:
My front-end is on a different domain -- Sails runs on localhost:1337 while my UI runs on localhost:8100.
Here's what my /api/config/cors.js looks like:
module.exports.cors = {
allRoutes: true,
origin: '*',
credentials: true,
// methods: 'GET, POST, PUT, DELETE, OPTIONS, HEAD',
// headers: 'content-type'
};
I'm using angular on the front-end, and the subsequent requests are using withCredentials: true -- do I need to add this to the login request too? Must I send the username/email along in the request also?
How do I allow all my subsequent requests for data authenticated after login?
If your frontend application has as a different origin than your backend application the AJAX requests will not include the session cookie by default.
If you are using jQuery:
$.ajax({
url: a_cross_domain_url,
xhrFields: {
withCredentials: true
}
});
This option has to be used for all AJAX requests, so the server can treat them as belonging to the same session.
You also have to configure the server side to allow CORS requests.

CORS could not get fixed even after adding Access-Control-Allow-Origin in service

I tried to access a Rest service which was hosted in different domain from mine through an ajax call and I got "CORS" error in the firebug.
After researching about this problem, I figured out that the service needs to be changed by adding Access-Control-Allow-Origin to * in the response header. I did that in the service as well.
public Response search(String expression) {
return Response.ok() //200
.entity(ConnectionUtils.query(expression))
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.header("Access-Control-Allow-Headers", "Content-Type,Accept")
.allow("OPTIONS").build();
}
Above method is the implementation class of below service interface:
#POST
#Path("/search")
public Response search(String expression);
I tried to post a request to this url through chrome advanced rest client, I am getting the response as well. Also the response header shows that the Access-Control-Allow-Origin has been set properly as well. Please refer to below screenshot of chrome client:
If you see above, the response headers has been changed.
But my below ajax call always return CORS error:
"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://192.168.1.100:8080/cqs-1.0-SNAPSHOT/services/services/cqs/search. This can be fixed by moving the resource to the same domain or enabling CORS."
$.ajax({
type: 'POST',
url: "http://192.168.1.100:8080/cqs-1.0-SNAPSHOT/services/services/cqs/search",
crossDomain: true,
dataType: 'json',
data: {"offer.offer.offerId.USSellerId": {$gt: 0}},
headers: {
"Accept": "application/json",
contentType: "application/json"
},
success: function (data) {
console.log(data);
},
error: function (data) {
console.log(data.statusText);
console.log(data.name);
}
});
Don't use CORS. It's fraught with peril. Rather, set up a simple reverse proxy on the server using Apache (or similar). Once you set it up, all requests from your app can go back to your domain/port thus not triggering any OPTION methods from the browser. Meanwhile your reverse proxy can redirect requests based on the path in the url, which is completely up to you on how you want to configure.
In my case, I just had a simple case of needing to access 2 different ports on the same server. Port 8080 (tomcat) was serving my GWT UI and REST requests from my GWT pages (using RestyGWT) were needing to hit port 9000 (Play framework port). Due to the different ports, CORS was required to deal with the OPTION 'preflight' checks that the browser was doing.
To solve this I just setup my URLs as having either /ui path or a /api path.
Since the domain/port was the same, and my proxy could easily redirect transparently to the correct port (api -> 9000 and ui -> 8080) there was no longer any need for CORS.
CORS has a lot of issues in my experience including cookies. It's really better to avoid it using a reverse proxy.
If you need more details, I can post more - let me know.
JR

handling a redirect from a cross-origin post in AJAX

We are trying to create a RESTful API that will be hosted on server x.foo.com. Client html applications (built in jquery) will be hosted on y.foo.com.
I am dealing with cross-domain issues by setting the Access-Control-Allow-Origin header as described here http://www.w3.org/TR/cors/.
So far so good, and I can now successfully make AJAX calls from host y to host x.
However, I ran into a gotcha with POST requests. The typical response to a post request is a redirect. However, the XMLHttpRequest object will not follow cross domain redirects, thus resulting in a failed call.
// Hosted on y.foo.com
$.ajax({
type: "POST",
url : http://x.foo.com/myapp/",
success: function(data) {
alert("success!");
}
});
// Return status: 302
// (Which errors out in firebug)
Anyone know of any techniques to handle the redirect (to a resource on server x) that I get from this post for a client hosted on y?
How about the client sends a special header for AJAX requests, and depending on whether it's an AJAX request or not, you can change the response instead of doing a redirect.

Resources