Handling data privacy in serverless analytics APIs

Creating an analytics dashboard where each user is able to access only certain parts of the data is really easy with Tinybird. You don't need to build anything specific from scratch. Tinybird provides dynamic API Endpoints, including specific security requirements per-user.

The serverless approach to real-time analytics

Let's assume you have just two components - the simplest possible stack:

  • A frontend application: Code that runs in the browser.
  • A backend application: Code that runs in the server and manages both the user authentication and the authorization. Very probably, the backend will also expose an API from where the frontend fetches the information needed.

This guide covers the different workflows that will handle each user operation with the right permissions, by integrating your backend with Static Tokens in a very simple way.

Create Tokens on user sign-up

The only thing you need (to ensure that your users have the right permissions on your data) is a created Tinybird Static Token every time you create a new user in your backend.

Creating a Token with filter scope
TOKEN=<your_token>

curl -H "Authorization: Bearer $TOKEN" \
  -d "name=user_692851_token" \
  -d "scope=PIPES:READ:ecommerce_example" \
  -d "scope=DATASOURCES:READ:events:user_id=692851" \
  https://api.tinybird.co/v0/tokens/

Use a Token with the right scope. Replace <your_token> with a Token whose scope is TOKENS or ADMIN.

This Token will let a given user query their own transactions stored in an events Data Source and exposed in an ecommerce_example API Endpoint.

Some other noteworthy things you can see here:

  • You can give a name to every Token you create. In this case, the name contains the user_id, so that it's easier to see what Token is assigned to each user.
  • You can assign as many scopes to each Token as you want, and DATASOURCES:READ:datasource_name and PIPES:READ:pipe_name can take an optional SQL filter (like this example does) to restrict the rows that queries authenticated with the Token will have access to.

If everything runs successfully, your call will return JSON containing a Token with the specified scopes:

Creating a Token with filter scope: Response
{
    "token": "p.eyJ1IjogImI2Yjc1MDExLWNkNGYtNGM5Ny1hMzQxLThhNDY0ZDUxMWYzNSIsICJpZCI6ICI0YTYzZDExZC0zNjg2LTQwN2EtOWY2My0wMzU2ZGE2NmU5YzQifQ.2QP1BRN6fNfgS8EMxqkbfKasDUD1tqzQoJXBafa5dWs",
    "scopes": [
        {
            "type": "PIPES:READ",
            "resource": "ecommerce_example",
            "filter": ""
        },
        {
            "type": "DATASOURCES:READ",
            "resource": "events",
            "filter": "user_id=692851"
        }
    ],
    "name": "user_692851_token"
}

All the Tokens you create are also visible in your Workspace > Tokens page in the UI, where you can create, update and delete them.

Modify Tokens when user permissions are changed

Imagine one of your users is removed from a group, which makes them lose some permissions on the data they can consume. Once that is reflected in your backend, you can update the user admin Token accordingly as follows:

Modify an existing Token
TOKEN=<your_token>
USER_TOKEN=<user_token>

curl -X PUT \
  -H "Authorization: Bearer $TOKEN" \
  -d "name=user_692851_token" \
  -d "scope=PIPES:READ:ecommerce_example" \
  -d "scope=DATASOURCES:READ:events:user_id=692851 and event in ('buy', 'add_item_to_cart')" \
  https://api.tinybird.co/v0/tokens/$USER_TOKEN

Pass the Token you previously created as a path parameter. Replace <user_token> by the value of token from the previous response, or copy it from the UI.

In this example you'd be restricting the SQL filter of the DATASOURCES:READ:events scope to restrict the type of events the user will be able to read from the events Data Source. This is the response you'd see from the API:

Modify an existing Token: Response
{
    "token": "p.eyJ1IjogImI2Yjc1MDExLWNkNGYtNGM5Ny1hMzQxLThhNDY0ZDUxMWYzNSIsICJpZCI6ICI0YTYzZDExZC0zNjg2LTQwN2EtOWY2My0wMzU2ZGE2NmU5YzQifQ.2QP1BRN6fNfgS8EMxqkbfKasDUD1tqzQoJXBafa5dWs",
    "scopes": [
        {
            "type": "PIPES:READ",
            "resource": "ecommerce_example",
            "filter": ""
        },
        {
            "type": "DATASOURCES:READ",
            "resource": "events",
            "filter": "user_id=692851 and event in ('buy', 'add_item_to_cart')"
        }
    ],
    "name": "user_692851_token"
}

Delete Tokens after user deletion

Whenever a user is removed from your system, you should also remove the Token from Tinybird. That will make things easier for you in the future.

Remove a token
TOKEN=<your_token>
USER_TOKEN=<user_token>

curl -X DELETE \
  -H "Authorization: Bearer $TOKEN" \
  https://api.tinybird.co/v0/tokens/$USER_TOKEN

If the Token is successfully deleted, this request will respond with no content and a 204 status code.

Refresh Tokens

It's a good practice to change Tokens from time to time, so you can automate this in your backend as well. Refreshing a Token requires executing this request for every one of your users:

Refresh a token
TOKEN=<your_token>
USER_TOKEN=<user_token>

curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  https://api.tinybird.co/v0/tokens/$USER_TOKEN/refresh

Next steps

Updated