{"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":16,"created_at":"2015-11-23T07:53:58Z","updated_at":"2015-11-30T12:18:07Z","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\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**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/160613867","html_url":"https://github.com/SocketCluster/socketcluster/issues/137#issuecomment-160613867","issue_url":"https://api.github.com/repos/SocketCluster/socketcluster/issues/137","id":160613867,"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-11-30T12:18:07Z","updated_at":"2015-11-30T12:18:07Z","body":"@mattkrick I guess grouping errors based on action is not that important. I think the benefits of having a lot of different error types outweighs the inconvenience of having to know about them - So long as they are documented.\r\n\r\nIf a user wants to know if a specific action failed (for any reason), they can just provide a callback to whatever method they're using. E.g. ```socket.publish('channelName', data, callback)``` - All errors would be passed to that callback (regardless of the error type).\r\n\r\nEffectively, the 'error' handler will be useful for adding generic error-handling logic (based on error types), while the callback will be useful for handling failures related to specific actions (without regard for error types). So actually this setup is quite flexible.\r\n\r\nWe could lump together publish and emit timeout errors under the same 'AckTimeoutError', but this is different from the heartbeat/ping timeout.\r\n\r\nThe ping timeout might happen beacause of a poor/lost connection. The ackTimeout is separate and it can be caused by two things:\r\n\r\n- Poor/lost connection\r\n- The server is taking too long to process the action (even though the connection itself is fine)\r\n\r\nWe could have a single 'AckTimeoutError' or have separate timeout errors for each type of action: 'PublishTimeoutError', 'EmitTimeoutError'. Maybe the second case is better; these are two different error cases and will often require different handling."}}