Documentation

Pairing with Scatter

This is currently optional to provide backwards compatibility, but will become mandatory in future releases of Scatter without notice. Some features such as the action permission system is already disabled for non-paired connections. It is best to handle this now than to have to handle it later when Scatter updates break your application.

Pairing is what Scatter does to allow users to link applications to their Scatter. It's how Scatter validates that an origin/app is who they say they are. There's two type of pairing that occur:

  • Passthrough Pairing - This type of pairing does not prompt the user to pair with your application, it only checks if the user has accepted it in the past. This is generally used on the socket.onopen call so that if it returns true you can do things like identityFromPermissions which will check if the user has also already provided an Identity before. If you try to do this without a passthrough it will prompt the user to accept the pairing before hitting that API route and cause a bad user-experience.
  • Non-Passthrough Pairing - When the passthrough parameter is false it will always prompt the user to permit a pairing if one doesn't exist. You should generally be checking for a pairing before every API request as it's overhead is minimal and it will make it so that you don't have to request a pairing until a user actually does something with Scatter ( like getIdentity ). If a pairing already exists and your key matches then the user will not be prompted.

There's also a few more things you need to know about.
  • Application Key ( appkey ) - This key is used to uniquely identify your application in a user's Scatter. When you first pair you send an unhashed version of this randomly generated key which is prefixed with appkey: along with the pairing request, and on all subsequent requests you send it hashed. Do not use a pre-defined application key. Generate it randomly for every user on their machine alone and never send it to any servers. It is not for you, it is for them.
  • Nonce ( nonce ) - This nonce should be a randomly generated alphanumeric ID of 24 char length. When sent to Scatter this nonce is always hashed. Do not use incremental ids for this, make them randomly generated or else you will break the security of the pairing link permission system and allow other applications to mimic your application and send requests in your name
  • Next Nonce ( nextNonce ) - This is the same as the nonce but is sent in clear text ( unhashed ) along with every request, and is then stored hashed in your application to be sent with the next request.
  • If the appkey, nonce, or nextNonce do not match what the user's Scatter has on file, the pairing link for your application is invalidated and the user will be requested to re-pair with your application before interacting with it again.
  • Hashing - The hashing algorithm Scatter uses to verify requests is sha256 in hex format.


When First Connecting

Let's look at a passthrough pairing for when we're first connecting.

// We're doing this when the socket opens,
// but only after we've sent back the connection ACK
socket.onopen = () => {
    socket.send('40/scatter');

    // This should be a state variable on your class,
    // and not only kept within this function's scope as
    // we will need it later.
    appkey = appkeyIsStored()
        ? getStoredHashedAppkey()       // Example: 867f01b41e264ac3a750e0ec7b13a729d6741f211181b126c41c6529073096df
        : 'appkey:'+random();           // Example: appkey:bt2gbcerb24quj56mp5jsrqr

    const json = {
        plugin:'YOUR_APP',
        data:{
            appkey,
            origin:'YOUR_APP',
            passthrough:true,
        }
    }

    socket.send('42/scatter,' + JSON.stringify(['pair', json]));

}

Breaking it down
  • First things first we always do this only after sending back the 40/scatter ACK.
  • The appkey that we're sending is based on whether your application has an appkey stored within it or not.
    If it does not have one then we send the unhashed value, if it does then we send the hashed value.
  • The plugin and data.origin values are required with every request sent to Scatter. In the case of a single app you should put your application's name in both values. If you are running an app explorer you can put your plugin name as your app name and sub-app names as the origin. ( note that the user will need to create pairings for each sub-app in that case ). plugin and origin must be the same across all your requests to Scatter.

Let's look at that funky socket.send() line. What's actually happening there is we're just appending a payload to the event message and it's event type.
I think the best way to explain this is to show it in the string of JSON that is actually sent through the socket.

Notice that this isn't valid JSON!
When receiving API messages you will need to remove the 42/scatter, before parsing the JSON.

"42/scatter, [
    pair, <-- this is the EVENT ( remember socket message type 2? )
    {     <-- This is the PAYLOAD
        plugin:'YOUR_APP',
        data:{
            appkey:'appkey:bt2gbcerb24quj56mp5jsrqr',
            origin:'YOUR_APP',
            passthrough:true,
        }
    }
]"


Putting this into a function

We're going to use this in multiple places, so it's best to put it into a function.

function pair(passthrough = false){
    return new Promise((resolve, reject) => {
        pairingPromise = {resolve, reject};
        const json = {...};
        socket.send('42/scatter,' + JSON.stringify(['pair', json]))
    })
}

Breaking it down
  • We're wrapping this function in a Promise to resolve it later.
  • We're also setting the pairingPromise to this Promise as well to hold onto it for when it resolves from the socket.onmessage handler.
  • The rest of the method is identical to what we laid out before.


Rekeying

Sometimes when you try to pair Scatter will send you back a rekey message telling you that either the user removed the pair/link permissions, or that one of your authorization values is out of sync ( appkey, nonce, nextNonce ).

We'll talk about this further in the next section Catching Responses