Pusher Custom Authentication
Tags: pusher, javascript
Published: 2018-05-29 13:15:07

---
title: Pusher Custom Authentication
published: true
description: How to write custom authorisers for Pusher
cover_image: https://thepracticaldev.s3.amazonaws.com/i/5a7b4iyqx7hq58dspfjh.jpg
tags: pusher, javascript
series: [pusher contest]
---

Last week I entered the first ever dev.to contest and submitted a serverless multiplayer clicker game. 

### [It would be awesome to get your ❤️ & 🦄 on my entry-post](https://dev.to/kayis/startup-clix-serverless-pusher-contest-entry-written-in-javascript-1jai)

I'd also like to give you some know-how in return.

# Custom Authentication for Pusher

Pusher allows for custom authorisers that can save you a bunch of requests.

## What

An authoriser for Pusher is just a function that takes a `context`, a `socketId` and a `callback` as arguments.

```javascript
function customAuth(context, socketId, callback) {
  ... 
}
```

It gets called by the Pusher client when you try to join a private or a presence channel.

The regular implementation sends a HTTP request to your backend and you have to return a token that the client will use to connect to Pusher.

The `socketId` is the current socket ID of the client.

The `callback` needs to be called when the authentication is done.

```javascript
// The first argument needs to be false if everything went well
// and the second one needs to include the credentials from the server
callback(false, authCredentials);

// The first argument needs to be true if the authentication failed
// and the second one can be a error description
callback(true, errorText);
```

## Why

The authoriser function is called every time a client subscribes to a private or presence channel and the default one sends a HTTP request every time, so depending on the amount of channels one client joins in short time it could be a good idea to consolidate these requests.

Also, as in the game I created, it could be that the client gets the information about which channel to join from the server. So you would end up with one request to get the channel and one request to authenticate it. With a custom authoriser you could create the `authCredentials` in the same request that chooses the channel.

## How

### Regular Auth

The regular auth procedure is as follows:

1. The client connects to Pusher and gets a `socketId`
2. The client tries to subscribe to a private or presence channel
3. The client sends its `socketId` and the `channelName` to the server (your server, not the Pusher server)
4. The server asks the Pusher API to authenticate the `socketId` for the `channelName`
5. The Pusher API creates `authCredentials` which get send back to the client
6. The client uses the `authCredenatials` to subscribe to the channel

The server side authentication looks like this

```javascript
const authCredentials = pusher.authenticate(
  socketId,
  channelName,
  {user_id: socketId}
);
```

The arguments can come in via HTTP query parameters or body, the `authCredentials` need to be sent back to the client via HTTP.

### Custom Auth

A custom version, like I used in my game, could look different.

Before

1. The client connects to Pusher and gets a `socketId`
2. The client requests a `channelName` from the server
3. The client gets a `channelName` from the server
4. The client tries to subscribe to the Pusher channel with the `channelName`
5. The client gets `authCredentials` from the server
6. The client subscribes to the Pusher channel with `authCredentials`

After

1. The client connects to Pusher and gets a `socketId`
2. The client requests a `channelName` from the server
3. The client gets a `channelName` and `authCredentials` from the server
4. The client subscribes to the Pusher channel with the `authCredentials`

So we need two new parts. A new authoriser, that wouldn't call the server but use some local data for authentication and a way to get the `channelName` and `authCredentials` from the server in one request.

Lets start from the back, how to get the two informations from the server.

For this we could add a new method to the Pusher client.

```javascript
pusher.subscribeServerChannel = function() {
  const {socket_id} = pusher.connection;

  return fetch("/getChannel?socketId=" + socket_id)
  .then(r => r.json())
  .then(({channelName, authCredentials}) => {
    // used by the authoriser later
    pusher.config.auth.preAuth[channelName] = authCredentials;

    // calls the autoriser
    return pusher.subscribe(channelName);
  })
};
```

This function tries to subscribe to a channel it requests from the server (your back-end). The `GET /getChannel` endpoint needs the `socketId` to create the `authCredentials` then `channelName` will also be created server side.

Next we need the new authoriser, also client side.

First get the old ones and add the new one to them. All _before_ we create a connection.

```javascript 
const supportedAuthorizers = Pusher.Runtime.getAuthorizers();

supportedAuthorizers.preAuthenticated = function(context, socketId, callback) {
  const { authOptions, channel } = this;

  // getting the credentials we saved in subscribeServerChannel
  const authCredentials = authOptions.preAuth[channel.name];

  if (authCredentials) return callback(false, authCredentials);

  callback(true, "You need to pre-authenticate for channel: " + channel.name);
};

Pusher.Runtime.getAuthorizers = () => supportedAuthorizers;

// Later when the connection is created

const pusher = new Pusher(APP_KEY, {
  auth: {
    preAuth: {} // where the auth credentials will be stored
  },
  // set the transport to the new authoriser
  authTransport: "preAuthenticated",
});
```

Last but not least the server endpoint that creates the `channelName` and handles authentication.

```javascript
server.get("/getChannel", (req, res) => {
  const {socketId} = req.query;
  const channelName = "private-" + Math.random();

  const authCredentials = pusher.authenticate(socketId, channelName, {user_id: socketId});

  res.send({channelName, authCredentials});
});
```

On the client we can now simply call `subscribeServerChannel` and get a pusher channel in return.

```javascript
  const pusher = new Pusher(APP_KEY, {
    auth: { preAuth: {} },
    authTransport: "preAuthenticated",
    ...
  });

  pusher.connection.bind("connected", async () =>
    const channel = await pusher.subscribeServerChannel();
    channel.bind("my:event", ...);
  );
```

And that's basically it.

You make one request and get all the data you need to join the channel with the client.

## Conclusion

The Pusher client is a very flexible piece of software that allows you to modify authentication-flow to your liking. This eases integration rather much and allows for some performance tweaks in the long run.

## Contest

Also, if you liked this post:

### [I would appreciate your ❤️ & 🦄 on my entry-post](https://dev.to/kayis/startup-clix-serverless-pusher-contest-entry-written-in-javascript-1jai)