{"action":"created","issue":{"url":"https://api.github.com/repos/SocketCluster/socketcluster/issues/137","labels_url":"https://api.github.com/repos/SocketCluster/socketcluster/issues/137/labels{/name}","comments_url":"https://api.github.com/repos/SocketCluster/socketcluster/issues/137/comments","events_url":"https://api.github.com/repos/SocketCluster/socketcluster/issues/137/events","html_url":"https://github.com/SocketCluster/socketcluster/issues/137","id":118334242,"number":137,"title":"SocketCluster v4 RFC","user":{"login":"jondubois","id":1187447,"avatar_url":"https://avatars.githubusercontent.com/u/1187447?v=3","gravatar_id":"","url":"https://api.github.com/users/jondubois","html_url":"https://github.com/jondubois","followers_url":"https://api.github.com/users/jondubois/followers","following_url":"https://api.github.com/users/jondubois/following{/other_user}","gists_url":"https://api.github.com/users/jondubois/gists{/gist_id}","starred_url":"https://api.github.com/users/jondubois/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jondubois/subscriptions","organizations_url":"https://api.github.com/users/jondubois/orgs","repos_url":"https://api.github.com/users/jondubois/repos","events_url":"https://api.github.com/users/jondubois/events{/privacy}","received_events_url":"https://api.github.com/users/jondubois/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":21,"created_at":"2015-11-23T07:53:58Z","updated_at":"2015-12-02T09:27:00Z","closed_at":null,"body":"This is the plan for SC version 4 - The main goal of this release will be to improve the flow related to:\r\n\r\n- Authentication\r\n- Authorization\r\n- Middleware\r\n- Error handling\r\n- Token expiry\r\n- Channel subscriptions\r\n\r\n... And how they relate/interact with one another.\r\n\r\nFeel free to post comments here if you wish to add something or want to make suggestions.\r\nThis release might break some stuff, but we should try to keep it mostly compatible with previous versions (so migration shouldn't be too difficult).\r\n\r\n**Current changes proposal** - This is open to review/changes\r\n\r\n- Make middleware APIs consistent across different middleware types. \"Currently, each one has different args. Let's offer two: a request object and a next function.\r\nThe request object's properties are populated based on the type of middleware.\r\nFor example, subscription has a socket and channel prop.\" - @mattkrick\r\n\r\n- Add a private (not visible to SC users) ```MIDDLEWARE_AUTHENTICATE``` middleware which will sit in front of all other middleware types (and thereby intercept all inbound messages/actions from the client) and its purpose will be to check the JWT token expiry - If it finds that the token has expired, it will remove the (expired) token from the server-side socket (so the ```socket.getAuthToken()``` method will return null/undefined) and emit a ```deauthenticate``` event on the client-side socket. It will also add an ```authTokenError``` property to the req object. This middleware will not block the request (even if token is expired), it will just pass it along to the following middleware and that middleware will be the one to decide whether or not the block the request/event.\r\n\r\n- Middleware errors can be triggered by the developer by passing a string or error to the ```next()``` callback inside middleware and will cause the 'error' event to be triggered on the client socket. Errors will have a name (and optionally, a type) property which will identify the error.\r\nE.g.\r\n```js\r\nsocket.on('error', function (err) {\r\n  if (err.name == 'SubscribeError') {\r\n    console.log(err.type); // -> 'AuthTokenError'\r\n    console.log(err.message); // -> 'Not allowed to subscribe to the foo channel'\r\n    console.log(err.channel); // -> 'foo'\r\n    // ...\r\n  } else if (err.name == 'PublishError') {\r\n    // ...\r\n  } else if (err.name == 'EmitError') {\r\n    // ...\r\n  } else {\r\n    // ...\r\n  }\r\n});\r\n```\r\nThe user can customize the properties of the Error object from the server by passing an object to the ```next()``` callback on the server. E.g:\r\n\r\n```js\r\nnext({\r\n  type: 'AuthTokenError',\r\n  message: 'Could not subscribe to ' + req.channel + ' without a valid auth token'\r\n});\r\n```\r\n\r\nThe error provided can also be a custom error object:\r\n\r\n```\r\nnext(new MyCustomError('Custom error'));\r\n// OR\r\nnext(req.authTokenError)\r\n```\r\n\r\nIf the user provides a string (allowed for backwards compatibility reasons), it will be cast into an error behind the scenes (maybe on the client-side).\r\n\r\n- Will need to remove the 'subscribeFail' event on the client since subscribe errors will now be passed to the 'error' event instead.\r\n\r\n- Errors emitted on the client should always be ```Error``` objects and never strings. If errors originated from middleware on the server (as strings), we should rehydrate them to be ```Error``` objects when on the client-side.\r\n\r\n- The subscribe method should accept an optional options argument ```socket.subscribe(channelName, [options])``` - This options object could have a ```waitForAuth``` (boolean) property which could allow the user to specify that this channel should wait for the socket to be authenticated before trying to subscribe.\r\n\r\n- The emit method should also accept an optional options argument ```socket.emit(event, data,[options], [callback])``` with a ```waitForAuth``` (boolean) property.\r\n\r\n- Passing an error to the emit response ```respond(err)``` should not trigger an 'error' event on the client-side. It's a business logic (soft) error and not a socket error.\r\n\r\n- Implement client-initiated and server-initiated deauthentication. See https://github.com/SocketCluster/socketcluster/issues/137#issuecomment-160970767\r\n\r\n- Will need to make sure that all errors throughout SC (client and server) have a appropriate types (and should never be strings - Always inherit from Error object) - Example client-side timeout errors: https://github.com/SocketCluster/socketcluster/issues/137#issuecomment-160613867 \r\n\r\n**Discussions**\r\n\r\n- https://github.com/SocketCluster/socketcluster-server/issues/1\r\n- https://github.com/SocketCluster/socketcluster-client/pull/28\r\n\r\nThanks to @mattkrick for all the input."},"comment":{"url":"https://api.github.com/repos/SocketCluster/socketcluster/issues/comments/161233173","html_url":"https://github.com/SocketCluster/socketcluster/issues/137#issuecomment-161233173","issue_url":"https://api.github.com/repos/SocketCluster/socketcluster/issues/137","id":161233173,"user":{"login":"jondubois","id":1187447,"avatar_url":"https://avatars.githubusercontent.com/u/1187447?v=3","gravatar_id":"","url":"https://api.github.com/users/jondubois","html_url":"https://github.com/jondubois","followers_url":"https://api.github.com/users/jondubois/followers","following_url":"https://api.github.com/users/jondubois/following{/other_user}","gists_url":"https://api.github.com/users/jondubois/gists{/gist_id}","starred_url":"https://api.github.com/users/jondubois/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jondubois/subscriptions","organizations_url":"https://api.github.com/users/jondubois/orgs","repos_url":"https://api.github.com/users/jondubois/repos","events_url":"https://api.github.com/users/jondubois/events{/privacy}","received_events_url":"https://api.github.com/users/jondubois/received_events","type":"User","site_admin":false},"created_at":"2015-12-02T09:27:00Z","updated_at":"2015-12-02T09:27:00Z","body":"@mattkrick Yes, I forgot to mention that, it will remove the token from the client at step 5.\r\n\r\nI think at this stage token renewal should be handled by the client - This offers the most flexibility (and there is an infinite number of ways to do this). It's just a matter of the user calling ```socket.setAuthToken(...)``` from the server.\r\n\r\nYes it's true that the solution I proposed has the same problem as yours ultimately. Also, it's true that a setInterval solution probably wouldn't be affected by a user changing time on his machine (I guess it depends on how it's implemented). I guess I like the approach I described more because the behaviour is more predictable and I prefer to keep the client lightweight."}}