Scoping with signed search

Use signed search to fetch bookings for a customer on the frontend without an app API token

Signed search is a way to fetch bookings for a customer on the frontend without providing an app API token.

Sometimes you need to fetch a list of bookings scoped based on a customer in your system. This is a typical marketplace scenario, where Timekit resources are your vendors and you also have a customer user role. With signed search you can generate a simple token used to verify that a user should access certain booking data. A signed search request is an alternative to using an app API key with basic authentication - making it usable on the frontend.

Timektit doesn't have a first class customer user role, but allow you to add customer information and meta data to a booking. With the search feature, you can search based on customer.email or meta.*, where * is a customer ID or whatever you use to identify a customer. However a typical search query is not safe to use on the frontend because 1. it requires the use of your app API key and 2. a user can just change the email to something else and get access to something they shouldn't. With signed search, you can cryptographically sign the part of the search query used to identify the customer, scoping the request to only be available to one specific customer. In a way the signed part of the request (the token) is equal to an access token with a limited scope that is safe to expose to your user.

You can generate a token once on your end and use it as many times as you want - it doesn't expire. It is just a cryptographic way to prove that a customer has access to some given data. It is important that you authenticate the customer yourself and make sure you don't expose the wrong token/signed request to the wrong customer.

Generating a token

The token needed to sign the request is generated using JWT. If you have no idea what JWT is, please have a look here to learn more. The token never expires, so you can generate it once and store it in your database.

Usually you will use a library to generate the JWT token, depending on which programming language you are using. Algorithmically, generating the token looks something like this:

HMACSHA256(
    base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
    'live_api_key_7nzvc7wsBQQISLeFSVhROys9V1bUJ1z7'
);

When generating a token for a signed request, you need to provide 2 things in the payload: An app slug and the data identifying the user (customer.email or meta.something). It looks like this:

{
  "app_slug": "my-app-6165",
  "customer.email": "[email protected]"
}

Or with meta data:

{
  "app_slug": "my-app-6165",
  "meta.customer_id": "12345"
}

Signing the request

When you sign a search request, you scope the request to a specific customer and do not need to provide your app API key. When you have generated your token, you can use it like this:

https://api.timekit.io/v2/bookings?search=customer.email:[email protected]&signed_search={JWT_TOKEN}

If someone tries to tamper with the URL and try to change the email, the JWT token will be invalidated and the request will fail.

You are welcome to add more search params to the query, as long as the signed part still matches for the customer:

https://api.timekit.io/v2/bookings?search=customer.email:[email protected];state:confirmed&signed_search={JWT_TOKEN}

Remember that you only need to generate the token once. It never expires as long as the way you identify the customer doesn't change.