Authorization

To keep your documents secure with maximum flexibility, TextSync provides an authorization strategy that integrates with your existing application.

Overview

Before sending any requests to TextSync’s servers, the TextSync client library sends an http POST request to an endpoint on your application server. Your server responds with a token describing the rights the user has to the document they are trying to access. The TextSync client library then includes this token with its request to TextSync server. The token is signed with a shared secret, so can’t be modified by the client without invalidating the token.

The TextSync Server Library

To simplify the token generation process, and to ensure the tokens generated by your application server are compatible with TextSync server, we recommend using the textsync-server-node library to generate tokens in your application. If you’re not using node.js for your server please get in touch and tell us what stack you’d like TextSync to support.

Examples

Deciding on document permissions is entirely up to you and will vary depending on how you’re using TextSync. There are some examples outlined below, but there are many more possibilities. In all examples, we define a `permissionsFn` which accepts a single argument - the id of the document the user wishes to access - and returns a promise resolving to the permissions the requester should be granted to the document. If you’re unsure about how to implement an authorization endpoint for your application get in touch.

Per User Document Permissions

Consider a model where every document is inaccessible, unless a user has specifically been given access permissions. The user’s document permissions are stored in a database in the following way:

1
2
3
4
5
6
7
8
{
  "name": "a person",
  "email": "a.person@domain.com",
  "documentPermissions": {
    "a person - personal document": ["read", "write"],
    "another person - collaborative document": ["read"],
  }
}

An example authorization endpoint for an express style application is shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
app.post('/textsync/tokens', (req, res) => {
  const permissionsFn = documentId => {
    const { userId } = req;
    return getUser(userId).then((user) => {
      const permissions = [];
      let documentPermissions = user.documentPermissions[documentID];
      if (!documentPermissions || !documentPermissions.length) {
        return permissions;
      }
      if('read' in documentPermissions) {
        permissions.push(TextSync.permissions.READ)
      }
      if('write' in documentPermissions) {
        permissions.push(TextSync.permissions.WRITE)
      }
      return permissions;
    });
  };


  let { body } = req;
  // set the tokenExpiry to 20 minutes
  let options = { tokenExpiry: 1 * 60 * 20 };
  textsync
    .authorizeDocument(body, permissionsFn, options)
    .then(token => {
      res.json(token);
    });
});

The permissionsFn function extracts the userIdfrom the request object and calls the asynchronous getUser* function. When the promise returned by the getUser function resolves, a permissions array is created. The array is populated with the permissions the user has for the document.

We pass the body of the request, the permissionsFn function and an (optional) options object to the authorizeDocument function. TheauthorizeDocument function returns a promise which resolves to thetoken object to return to the client.

* We’ve not included the specifics of retrieving the user as it’s not important for this example. Assume that function goes away to a database and retrieves a user object like the one above.

Publically Readable Documents

In this scenario, all documents are readable by anybody but only editable by their owner. Document specific metadata, including the id of the user who owns the document, are stored in a documents table in a database.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
app.post('/textsync/tokens', (req, res) => {
  const permissionsFn = documentId => {
    const { userId } = req;
    let permissions = [ TextSync.Permissions.READ ];
    return getDocumentData(documentId).then((document) => {
      if(document.owner === userId){
        permissions.push(TextSync.Permissions.WRITE);
      }
      return permissions;
    });
  };


  let { body } = req;
  // set the tokenExpiry to 20 minutes
  let options = { tokenExpiry: 1 * 60 * 20 };
  textsync
    .authorizeDocument(body, permissionsFn, options)
    .then(token => {
      res.json(token);
    });
});

Since all documents are readable by everybody the permissionsFn starts with an array containing READ permission. It then retrieves the document data user the getDocumentData* function and checks whether the user making the request is the document owner. If the requester is the document owner, WRITE permission is appended to the permissions array.

* We’ve not included the specifics of retrieving the document as it’s not important for this example. Assume that function goes away to a database and retrieves a document object.

Did you find this document useful?

We are always striving to create the most accurate and informative docs as possible. If there is something especially wrong (or right) here then please let us know.