Get express data via socket and HTTP authentication #233

Closed
zmikolaj opened this Issue Oct 19, 2016 · 12 comments

Comments

Projects
None yet
3 participants
@zmikolaj

zmikolaj commented Oct 19, 2016

Hi, how i can read data when socket connet that i set when you go to specife url?
app.get('/test', function(req,res) { req.socket.data = 'my data'; res.json({success: true}); });
and than in connection:
scServer.on('connection', function(socket) { console.log(socket.data); });

its that possible? Or its possible to set authdata in httpserver?

@jondubois

This comment has been minimized.

Show comment
Hide comment
@jondubois

jondubois Oct 19, 2016

Member

On the client side, you can pass custom query parameters:

socketCluster.connect({query: {someKey: someValue, anotherKey: anotherValue}});

^ You can pass the result of a previous HTTP get request as query parameters.
then, from the server-side, you can access those inside the 'connection' handler using something like:

var url = require('url'); // The Node.js core url module

// ... then inside the 'connection' handler:

var urlParts = url.parse(socket.request.url, true);
console.log(urlParts.query);

You can also do a similar thing using cookies.

Member

jondubois commented Oct 19, 2016

On the client side, you can pass custom query parameters:

socketCluster.connect({query: {someKey: someValue, anotherKey: anotherValue}});

^ You can pass the result of a previous HTTP get request as query parameters.
then, from the server-side, you can access those inside the 'connection' handler using something like:

var url = require('url'); // The Node.js core url module

// ... then inside the 'connection' handler:

var urlParts = url.parse(socket.request.url, true);
console.log(urlParts.query);

You can also do a similar thing using cookies.

@zmikolaj

This comment has been minimized.

Show comment
Hide comment
@zmikolaj

zmikolaj Oct 19, 2016

Ye, but i want to use passport.js with http server, so its possible to use setAuthToken in express?

Ye, but i want to use passport.js with http server, so its possible to use setAuthToken in express?

@jondubois

This comment has been minimized.

Show comment
Hide comment
@jondubois

jondubois Oct 19, 2016

Member

@trzyrazyzero

The best (and also simplest) way to do authentication in SC over HTTP is by creating and signing a new JWT token from express and sending it to the client-side (as a string in the response); then, once you get that token on the client side, you just need to add it to localStorage under the key 'socketCluster.authToken' (this is the default JWT localStorage key). SC will automatically pick it up from local storage when a new connection is created.

When you create and sign the JWT token inside your express route, you need to make sure that you use the same key as specified inauthKey when instantiating SocketCluster. Scroll down to the authKey option here for details: http://socketcluster.io/#!/docs/api-socketcluster (you can get it inside your worker.js using worker.options.authKey).

You can also change the default auth engine which SC uses on the client side if you have more advanced requirements (e.g. if you want to store and load the JWT from cookies instead of localStorage) but localStorage is the simplest approach and works the best on all modern devices.

Also, see this guide for more details: http://socketcluster.io/#!/docs/authentication

Member

jondubois commented Oct 19, 2016

@trzyrazyzero

The best (and also simplest) way to do authentication in SC over HTTP is by creating and signing a new JWT token from express and sending it to the client-side (as a string in the response); then, once you get that token on the client side, you just need to add it to localStorage under the key 'socketCluster.authToken' (this is the default JWT localStorage key). SC will automatically pick it up from local storage when a new connection is created.

When you create and sign the JWT token inside your express route, you need to make sure that you use the same key as specified inauthKey when instantiating SocketCluster. Scroll down to the authKey option here for details: http://socketcluster.io/#!/docs/api-socketcluster (you can get it inside your worker.js using worker.options.authKey).

You can also change the default auth engine which SC uses on the client side if you have more advanced requirements (e.g. if you want to store and load the JWT from cookies instead of localStorage) but localStorage is the simplest approach and works the best on all modern devices.

Also, see this guide for more details: http://socketcluster.io/#!/docs/authentication

@zmikolaj

This comment has been minimized.

Show comment
Hide comment
@zmikolaj

zmikolaj Oct 19, 2016

How i can create and sign new JWT token? And by sending you mean socket.emit with JWT token?

How i can create and sign new JWT token? And by sending you mean socket.emit with JWT token?

@jondubois

This comment has been minimized.

Show comment
Hide comment
@jondubois

jondubois Oct 19, 2016

Member

@trzyrazyzero Good question.
You can create the JWT token however you like (so long as it meets JWT specifications). In Node.js, you can use the jsonwebtoken module directly: https://www.npmjs.com/package/jsonwebtoken

or for convenience you can also use SC's internal worker.auth.signToken(...) function like this:

// Sample token data, don't store any sensitive/secret data here
// it will be signed but not encrypted.
  var myTokenData = {
    username: 'bob',
    language: 'English',
    company: 'Google',
    groups: ['engineering', 'science', 'mathematics']
  };

  // worker.options.authKey below is the key which SC uses internally to verify the token.
  
  worker.auth.signToken(myTokenData, worker.options.authKey, null, function (err, token) {
    // The token variable is the signed JWT token; you can send this to your frontend
    // however you like (e.g. in HTTP response). Once it reaches the frontend, you should
    // add it to localStorage under the key 'socketCluster.authToken'- By doing this, SC
    // will automatically pick up the JWT token when doing its handshake/connecting.
    console.log('JWT token:', token);
  });
Member

jondubois commented Oct 19, 2016

@trzyrazyzero Good question.
You can create the JWT token however you like (so long as it meets JWT specifications). In Node.js, you can use the jsonwebtoken module directly: https://www.npmjs.com/package/jsonwebtoken

or for convenience you can also use SC's internal worker.auth.signToken(...) function like this:

// Sample token data, don't store any sensitive/secret data here
// it will be signed but not encrypted.
  var myTokenData = {
    username: 'bob',
    language: 'English',
    company: 'Google',
    groups: ['engineering', 'science', 'mathematics']
  };

  // worker.options.authKey below is the key which SC uses internally to verify the token.
  
  worker.auth.signToken(myTokenData, worker.options.authKey, null, function (err, token) {
    // The token variable is the signed JWT token; you can send this to your frontend
    // however you like (e.g. in HTTP response). Once it reaches the frontend, you should
    // add it to localStorage under the key 'socketCluster.authToken'- By doing this, SC
    // will automatically pick up the JWT token when doing its handshake/connecting.
    console.log('JWT token:', token);
  });
@jondubois

This comment has been minimized.

Show comment
Hide comment
@jondubois

jondubois Oct 19, 2016

Member

@trzyrazyzero You can send the token to your frontend however you like... If you're creating the JWT from inside an express GET route, then you probably want to send it back as part of the HTTP response body - But then make sure that you handle the response on your frontend and add your token to localStorage using JavaScript localStorage.setItem('socketCluster.authToken', token).

Once the JWT token is added to localStorage under the socketCluster.authToken key, the rest is all handled automatically by SC. You will be able to get the token data from your SC socket on either the client or server by using socket.authToken (or the old way: socket.getAuthToken()).

Member

jondubois commented Oct 19, 2016

@trzyrazyzero You can send the token to your frontend however you like... If you're creating the JWT from inside an express GET route, then you probably want to send it back as part of the HTTP response body - But then make sure that you handle the response on your frontend and add your token to localStorage using JavaScript localStorage.setItem('socketCluster.authToken', token).

Once the JWT token is added to localStorage under the socketCluster.authToken key, the rest is all handled automatically by SC. You will be able to get the token data from your SC socket on either the client or server by using socket.authToken (or the old way: socket.getAuthToken()).

@jondubois

This comment has been minimized.

Show comment
Hide comment
@jondubois

jondubois Oct 19, 2016

Member

Note that worker.auth in the code sample above is just an instance of SC's server side auth engine - The default being https://github.com/SocketCluster/sc-auth/blob/master/index.js

In SC, you can swap out both the client side and server side auth engines with your own engines if you want but generally this isn't necessary.

To change the default server auth engine, you can use worker.setAuthEngine(myCustomServerAuthEngine) http://socketcluster.io/#!/docs/api-scworker

To change the default client auth engine, you need to pass a custom authEngine option when creating the socket on the client. E.g. socketCluster.connect({authEngine: myCustomClientAuthEngine})

Note that the server auth engine and the client auth engine are different (and serve different purposes).
The server auth engine needs to implement a signToken and a verifyToken function.
See default server auth engine here (as mentioned above): https://github.com/SocketCluster/sc-auth/blob/master/index.js

The client auth engine needs to implement a saveToken, removeToken and a loadToken function.
See default client auth engine here: https://github.com/SocketCluster/socketcluster-client/blob/master/lib/auth.js

Member

jondubois commented Oct 19, 2016

Note that worker.auth in the code sample above is just an instance of SC's server side auth engine - The default being https://github.com/SocketCluster/sc-auth/blob/master/index.js

In SC, you can swap out both the client side and server side auth engines with your own engines if you want but generally this isn't necessary.

To change the default server auth engine, you can use worker.setAuthEngine(myCustomServerAuthEngine) http://socketcluster.io/#!/docs/api-scworker

To change the default client auth engine, you need to pass a custom authEngine option when creating the socket on the client. E.g. socketCluster.connect({authEngine: myCustomClientAuthEngine})

Note that the server auth engine and the client auth engine are different (and serve different purposes).
The server auth engine needs to implement a signToken and a verifyToken function.
See default server auth engine here (as mentioned above): https://github.com/SocketCluster/sc-auth/blob/master/index.js

The client auth engine needs to implement a saveToken, removeToken and a loadToken function.
See default client auth engine here: https://github.com/SocketCluster/socketcluster-client/blob/master/lib/auth.js

@jondubois

This comment has been minimized.

Show comment
Hide comment
@jondubois

jondubois Oct 19, 2016

Member

I've updated the authentication page on the socketcluster.io website to link back to this discussion. See http://socketcluster.io/#!/docs/authentication

Member

jondubois commented Oct 19, 2016

I've updated the authentication page on the socketcluster.io website to link back to this discussion. See http://socketcluster.io/#!/docs/authentication

@jondubois jondubois closed this Jan 7, 2017

@jondubois jondubois referenced this issue in SocketCluster/sc-sample-inventory Jan 18, 2017

Open

How to make this use OAuth with Github #4

@jondubois jondubois changed the title from Get express data via socket to Get express data via socket and HTTP authentication Jul 31, 2017

@vaskevich

This comment has been minimized.

Show comment
Hide comment
@vaskevich

vaskevich Mar 10, 2018

To follow up on this, is there a good way to stub out the JWT logic? I'm using client-sessions to maintain sessions that are stored in an HTTP-only cookie. Instead of JWT-based auth, I'd just like to use the scServer.MIDDLEWARE_AUTHENTICATE middleware to handle reading the cookie and authenticating, without dealing w/JWT.

To follow up on this, is there a good way to stub out the JWT logic? I'm using client-sessions to maintain sessions that are stored in an HTTP-only cookie. Instead of JWT-based auth, I'd just like to use the scServer.MIDDLEWARE_AUTHENTICATE middleware to handle reading the cookie and authenticating, without dealing w/JWT.

@jondubois

This comment has been minimized.

Show comment
Hide comment
@jondubois

jondubois Mar 10, 2018

Member

@vaskevich You can bypass it entirely.

Because HTTP-only cookies cannot be explicitly accessed (saved/loaded) on the client-side, they don't fit into SC's standard authentication flow but you can still authenticate sockets at a lower level during the handshake phase. This ensures that sockets are authenticated before they are connected. It's slightly less flexible but very widely used and suitable for most cases.

When using HTTP-based authentication using cookies, you can read the cookie from the HTTP request that is associated with the WebSocket object on the server-side.

You can use the scServer.MIDDLEWARE_HANDSHAKE_WS to access the HTTP upgrade request (and read any attached cookies) to determine the authentication state of the socket; from that middleware you can either block the connection or attach custom properties to the req object (which you will be able to access later using the socket.request object); so you can do the authentication yourself without relying on SC's JWT authentication mechanism. The main downside to this approach is that the socket's authentication state cannot change throughout the life of a single socket connection; but that limitation is inherent to HTTP cookies - You can just reconnect the socket if you want to use a new cookie.

Alternatively you can use the scServer.MIDDLEWARE_HANDSHAKE_SC middleware; this is the SC-protocol handshake which happens after the native WebSocket (WS) handshake; after the socket has been created; it gives you a bit more flexibility when it comes to sending back things like custom errors.

Also, some apps authenticate by passing a token using a ?token=... querystring as part of the HTTP upgrade request URL; this follows the same principle as the cookie approach; and has the same downside that the authentication state cannot change throughout the lifetime of a single socket connection.

Member

jondubois commented Mar 10, 2018

@vaskevich You can bypass it entirely.

Because HTTP-only cookies cannot be explicitly accessed (saved/loaded) on the client-side, they don't fit into SC's standard authentication flow but you can still authenticate sockets at a lower level during the handshake phase. This ensures that sockets are authenticated before they are connected. It's slightly less flexible but very widely used and suitable for most cases.

When using HTTP-based authentication using cookies, you can read the cookie from the HTTP request that is associated with the WebSocket object on the server-side.

You can use the scServer.MIDDLEWARE_HANDSHAKE_WS to access the HTTP upgrade request (and read any attached cookies) to determine the authentication state of the socket; from that middleware you can either block the connection or attach custom properties to the req object (which you will be able to access later using the socket.request object); so you can do the authentication yourself without relying on SC's JWT authentication mechanism. The main downside to this approach is that the socket's authentication state cannot change throughout the life of a single socket connection; but that limitation is inherent to HTTP cookies - You can just reconnect the socket if you want to use a new cookie.

Alternatively you can use the scServer.MIDDLEWARE_HANDSHAKE_SC middleware; this is the SC-protocol handshake which happens after the native WebSocket (WS) handshake; after the socket has been created; it gives you a bit more flexibility when it comes to sending back things like custom errors.

Also, some apps authenticate by passing a token using a ?token=... querystring as part of the HTTP upgrade request URL; this follows the same principle as the cookie approach; and has the same downside that the authentication state cannot change throughout the lifetime of a single socket connection.

@vaskevich

This comment has been minimized.

Show comment
Hide comment
@vaskevich

vaskevich Mar 11, 2018

Gotcha, thanks for explaining. The issue with authentication state remaining constant during the connection isn't an issue for me.

On the implementation side, would you just recommend setting a custom auth engine that essentially does nothing on the client and server side? Would like to avoid sending/validating JWT tokens if they won't be at all used.

Gotcha, thanks for explaining. The issue with authentication state remaining constant during the connection isn't an issue for me.

On the implementation side, would you just recommend setting a custom auth engine that essentially does nothing on the client and server side? Would like to avoid sending/validating JWT tokens if they won't be at all used.

@jondubois

This comment has been minimized.

Show comment
Hide comment
@jondubois

jondubois Mar 11, 2018

Member

@vaskevich If you don't explicitly create a JWT with socket.setAuthToken() or manually add a JWT token inside the default localStorage location in socketCluster.authToken - Then it's not going to send anything. The JWT is only sent as part of the handshake it it exists and the SC server doesn't care at all if there is never any JWT.

Hypothetically you could swap out auth engines (on both client and server) to handle any kind of token from the client (except those set using HTTP-only cookies because they are intentionally not exposed to the client code).

Member

jondubois commented Mar 11, 2018

@vaskevich If you don't explicitly create a JWT with socket.setAuthToken() or manually add a JWT token inside the default localStorage location in socketCluster.authToken - Then it's not going to send anything. The JWT is only sent as part of the handshake it it exists and the SC server doesn't care at all if there is never any JWT.

Hypothetically you could swap out auth engines (on both client and server) to handle any kind of token from the client (except those set using HTTP-only cookies because they are intentionally not exposed to the client code).

@anywhere3d anywhere3d referenced this issue in SocketCluster/socketcluster-server May 11, 2018

Closed

Parsing data from socketcluster to express #27

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment