Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ pusher

This library supports end-to-end encryption of your private channels. This means that only you and your connected clients will be able to read your messages. Pusher cannot decrypt them. You can enable this feature by following these steps:

1. You should first set up Private channels. This involves [creating an authentication endpoint on your server](https://pusher.com/docs/authenticating_users).
1. You should first set up Private channels. This involves [creating an authorization endpoint on your server](https://pusher.com/docs/authenticating_users).

2. Next, generate your 32 byte master encryption key, encode it as base64 and pass it to the Pusher constructor.

Expand Down Expand Up @@ -278,17 +278,44 @@ pusher.trigger(["channel-1", "private-encrypted-channel-2"], "test_event", {

Rationale: the methods in this library map directly to individual Channels HTTP API requests. If we allowed triggering a single event on multiple channels (some encrypted, some unencrypted), then it would require two API requests: one where the event is encrypted to the encrypted channels, and one where the event is unencrypted for unencrypted channels.

### Authenticating private channels
### Authenticating users

To authorise your users to access private channels on Pusher Channels, you can use the `authenticate` function:
To authenticate users during sign in, you can use the `authenticateUser` function:

```javascript
const auth = pusher.authenticate(socketId, channel)
const userData = {
id: "unique_user_id",
name: "John Doe",
image: "https://...",
}
const auth = pusher.authenticateUser(socketId, userData)
```

The `userData` parameter must contain an `id` property with a non empty string. For more information see: <http://pusher.com/docs/authenticating_users>

### Terminating user connections

In order to terminate a user's connections, the user must have been authenticated. Check the [Server user authentication docs](http://pusher.com/docs/authenticating_users) for the information on how to create a user authentication endpoint.

To terminate all connections established by a given user, you can use the `terminateUserConnections` function:

```javascript
pusher.terminateUserConnections(userId)
```

Please note, that it only terminates the user's active connections. This means, if nothing else is done, the user will be able to reconnect. For more information see: [Terminating user connections docs](https://pusher.com/docs/channels/server_api/terminating-user-connections/).

### Private channel authorisation

To authorise your users to access private channels on Pusher Channels, you can use the `authorizeChannel` function:

```javascript
const auth = pusher.authorizeChannel(socketId, channel)
```

For more information see: <http://pusher.com/docs/authenticating_users>

### Authenticating presence channels
### Presence channel authorisation

Using presence channels is similar to private channels, but you can specify extra data to identify that particular user:

Expand All @@ -300,7 +327,7 @@ const channelData = {
twitter_id: '@leggetter'
}
};
const auth = pusher.authenticate(socketId, channel, channelData);
const auth = pusher.authorizeChannel(socketId, channel, channelData);
```

The `auth` is then returned to the caller as JSON.
Expand Down
10 changes: 10 additions & 0 deletions lib/auth.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
const util = require("./util")

function getSocketSignatureForUser(token, socketId, userData) {
const serializedUserData = JSON.stringify(userData)
const signature = token.sign(`${socketId}::user::${serializedUserData}`)
return {
auth: `${token.key}:${signature}`,
user_data: serializedUserData,
}
}

function getSocketSignature(pusher, token, channel, socketID, data) {
const result = {}

Expand All @@ -26,4 +35,5 @@ function getSocketSignature(pusher, token, channel, socketID, data) {
return result
}

exports.getSocketSignatureForUser = getSocketSignatureForUser
exports.getSocketSignature = getSocketSignature
71 changes: 69 additions & 2 deletions lib/pusher.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ const validateSocketId = function (socketId) {
}
}

const validateUserId = function (userId) {
if (typeof userId !== "string" || userId === "") {
throw new Error("Invalid user id: '" + userId + "'")
}
}

const validateUserData = function (userData) {
if (userData == null || typeof userData !== "object") {
throw new Error("Invalid user data: '" + userData + "'")
}
validateUserId(userData.id)
}

/** Provides access to Pusher's REST API, WebHooks and authentication.
*
* @constructor
Expand Down Expand Up @@ -103,9 +116,9 @@ Pusher.forCluster = function (cluster, options) {
* @param {String} socketId socket id
* @param {String} channel channel name
* @param {Object} [data] additional socket data
* @returns {String} authentication signature
* @returns {String} authorization signature
*/
Pusher.prototype.authenticate = function (socketId, channel, data) {
Pusher.prototype.authorizeChannel = function (socketId, channel, data) {
validateSocketId(socketId)
validateChannel(channel)

Expand All @@ -118,6 +131,60 @@ Pusher.prototype.authenticate = function (socketId, channel, data) {
)
}

/** Returns a signature for given socket id, channel and socket data.
*
* DEPRECATED. Use authorizeChannel.
*
* @param {String} socketId socket id
* @param {String} channel channel name
* @param {Object} [data] additional socket data
* @returns {String} authorization signature
*/
Pusher.prototype.authenticate = Pusher.prototype.authorizeChannel

/** Returns a signature for given socket id and user data.
*
* @param {String} socketId socket id
* @param {Object} userData user data
* @returns {String} authentication signature
*/
Pusher.prototype.authenticateUser = function (socketId, userData) {
validateSocketId(socketId)
validateUserData(userData)

return auth.getSocketSignatureForUser(this.config.token, socketId, userData)
}

/** Sends an event to a user.
*
* Event name can be at most 200 characters long.
*
* @param {String} userId user id
* @param {String} event event name
* @param data event data, objects are JSON-encoded
* @returns {Promise} a promise resolving to a response, or rejecting to a RequestError.
* @see RequestError
*/
Pusher.prototype.sendToUser = function (userId, event, data) {
if (event.length > 200) {
throw new Error("Too long event name: '" + event + "'")
}
validateUserId(userId)
return events.trigger(this, [`#server-to-user-${userId}`], event, data)
}

/** Terminate users's connections.
*
*
* @param {String} userId user id
* @returns {Promise} a promise resolving to a response, or rejecting to a RequestError.
* @see RequestError
*/
Pusher.prototype.terminateUserConnections = function (userId) {
validateUserId(userId)
return this.post({ path: `/users/${userId}/terminate_connections`, body: {} })
}

/** Triggers an event.
*
* Channel names can contain only characters which are alphanumeric, '_' or '-'
Expand Down
Loading