2

I'm trying to make a sock.js connection from the frontend to the vertx backend.

my initial try looked like this:

let token = '<the token>';
let data = {'Authorization' : 'Bearer ' + token};
let eb = new EventBus("http://localhost:8080/eventbus");
  eb.onopen = function () {
  eb.registerHandler('notifications', data, (err, msg) =>  {
    // handle the response
  });
}

this doesn't work since I need to send the auth data on EventBus creation, even though the official sock.js documentation states that this is not supported. Obviously now sending new EventBus("http://localhost:9090/eventbus", data) doesn't work either.

https://github.com/sockjs/sockjs-node#authorisation

my backend handler for this:

final BridgeOptions bridgeOptions = new BridgeOptions()
  .addOutboundPermitted(new PermittedOptions().setAddress("notifications"))

final SockJSHandler sockJSHandler = SockJSHandler.create(vertx).bridge(bridgeOptions, event -> {
  event.complete(true);
});

router.route("/eventbus/*").handler(ctx -> {
  String token = ctx.request().getHeader("Authorization"); // null
});
router.route("/eventbus/*").handler(sockJSHandler);

whatever I tried the header field Authroization is always null.

What is the standard way to authenticate the sock.js connection and register to an eventbus request in vertx?

Markus
  • 1,565
  • 6
  • 26
  • 57
  • Have you checked the [Requiring authorisation for messages](http://vertx.io/docs/vertx-web/java/#_requiring_authorisation_for_messages) section of the Vert.x Web doc? – tsegismont Jun 06 '17 at 15:14
  • yes, i've read the whole vertx docs – Markus Jun 06 '17 at 15:15
  • OK. I asked because your snippet does not look like you use the Vert.x Web authorization functionality. – tsegismont Jun 06 '17 at 15:17
  • i already tried the standard `JWTAuthHandler` approach I do on normal http requests, but the error is the same -> Token not found because I don't know how to send it from the frontend side correctly. the backend works fine – Markus Jun 06 '17 at 15:19
  • Have a look at the [vertx-examples repo](https://github.com/vert-x3/vertx-examples/blob/ea6e07945baa9abec6d6f4f98404c727cc8c342e/web-examples/src/main/java/io/vertx/example/web/angular_realtime/Server.java) to see the bridge auth in action. Please share a reproducer on GitHub if it still doesn't work. Thanks – tsegismont Jun 06 '17 at 20:06
  • i already looked at all the docs &examples. as i said, my problem is not on the backend side. my problem is the frontend. how to I send the token to the backend? – Markus Jun 06 '17 at 20:17

3 Answers3

7

SockJS uses WebSockets by default. You can't add custom headers (Authorization, etc) using JavaScript WebSocket API. Read this thread for more explanation.

I see 2 ways, how you can add authorization:

  1. Just add token parameter to URL:

    let eb = new EventBus("http://localhost:8080/eventbus?token=" + token);
    

    and here's how you can get it on a server:

    String token = ctx.request().getParam("token");
    
  2. Send authorization message after connecting to the server. It can be some JSON object, which contains token field.

I think, 1st option is enough, however, 2nd one can be harder to implement in terms of Event Bus and SockJS.

berserkk
  • 987
  • 6
  • 11
  • 1
    query params are not allowed in sockJS unfortunately (`Only basic urls are supported in SockJS`). And for your second method: that should work, but I imagine there is a better way to do it, because with this you wouldn't be able to use the standard vertx *AuthHandlers like `router.route("/eventbus/*").handler(jwtAuthHandler);` (or a similiar way like my test handler in my script above) – Markus Jun 07 '17 at 06:24
  • You can still use URL like `/eventbus/:token`. Not sure, if there is another easier method to do it. – berserkk Jun 07 '17 at 07:58
  • i don' really understand why, but yes it seems like i have to do some kind of hack based on one of your solutions. thanks – Markus Jun 07 '17 at 08:02
  • Keep in mind, that 2nd approach is good for raw WebSockets, but I'm unsure, if it's good for Event Bus and SockJS. – berserkk Jun 07 '17 at 08:05
  • yeah thx, I think I'll stick with the first approach for now, seems like the best way to go – Markus Jun 07 '17 at 08:10
  • How can we pass the token on Header? – RaiBnod Jun 12 '18 at 06:58
  • @RaiBnod you can't do it. Please check 1st paragraph. – berserkk Jun 12 '18 at 07:04
  • @RaiBnod sorry, I don't fully understand your issue. URL `/eventbus` works well? Can you show your code? – berserkk Jun 12 '18 at 23:10
  • Yes, /eventbus/* is working very well. On the eventbus instance creation, it is requesting URLs, /eventbus/info and then /eventbus///websocket. But when I change my EventBus instance creation URL as /eventbus/:accesstoken=, it request a URL, /eventbus/:access_token=/info, which doesn't exist. Here I need to protect the URL /eventbus/info with the Authentication. – RaiBnod Jun 13 '18 at 03:27
  • 3
    I have used latest SockJs and EventBus javascript packages and now it is solved with the above first method. :) P.S. use: https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.5/sockjs.min.js – RaiBnod Jun 13 '18 at 05:01
1

Since sending Authorization header is not possible, attaching a token query parameter (as described by @berserkk) is the way to go.

However, in some circumstances, it may be undesirable to send your main login token in plain text as a query parameter because it is more opaque than using a header and will end up being logged whoknowswhere. If this raises security concerns for you, an alternative is to use a secondary JWT token just for the web socket stuff.

Create a REST endpoint for generating this JWT, which can of course only be accessed by users authenticated with your primary login token (transmitted via header). The web socket JWT can be configured differently than your login token, e.g. with a shorter timeout, so it's safer to send around as query param of your upgrade request.

Create a separate JwtAuthHandler for the same route you register the SockJS eventbusHandler on. Make sure your auth handler is registered first, so you can check the web socket token against your database (the JWT should be somehow linked to your user in the backend).

0

I think best way to secure a web-socket is using CORS check

Cross Origin Resource Sharing is a safe mechanism for allowing resources to be requested

router.route().handler(CorsHandler.create(your host origin path).allowCredentials(true));

We can add more layer of security also using sockjs :

Allow events for the designated addresses in/out of the event bus bridge

 BridgeOptions opts = new BridgeOptions()
          .addInboundPermitted(new PermittedOptions().setAddressRegex(Constants.INBOUND_REGEXP));
Shasha
  • 669
  • 1
  • 8
  • 26
  • that doesn't help with authentication though? – Markus Jun 10 '17 at 19:21
  • are you talking about CORS check? – Shasha Jun 10 '17 at 19:23
  • i want to authenticate and authorize a user using sock.js, the standard rest way with header doesn't work. the suggested solution above or something similiar (see sock.js auth readme) works, but both feel very hacky. I dont know how your CORS solution would solve this? – Markus Jun 10 '17 at 19:27
  • Currently we are using CORS check to only open an websocket connection on our hosted application. So no one else can connect to websocket. So this is the starting point where we are blocking unwanted incoming requests. – Shasha Jun 10 '17 at 19:31
  • you can refer this link for more security on web socket : https://www.christian-schneider.net/CrossSiteWebSocketHijacking.html – Shasha Jun 10 '17 at 19:39
  • regarding security I understand the solution, but my situation is a bit different. Parts of my api are exposed via sock.js to any user. To know which user should receive which kinds of messages I need to do a normal auth flow. – Markus Jun 10 '17 at 19:43