Toloka documentation

Event authentication

The Toloka API sends event notifications that may contain sensitive data. It's important to prevent data leaks and ensure reliable data protection. To do this, make sure that requests are being sent through the Toloka API.

An additional header — Toloka-Signature — is used to confirm that the Toloka API is the source of notifications. The header is generated from request parameters. The HMAC Sha256 hashing algorithm is used for creating the signature.

Creating a secure subscription

When creating a subscription, add the secret_key parameter to the request body. It checks whether incoming requests were sent through the Toloka API. If the parameter was added, the additional Toloka-Signature header appears in the event notification.

Validating the notification sender

The Toloka-Signature header consists of three fields:

  • ts: The key generation time in Unix format (milliseconds).
  • v: The version of the key.
  • sign: A signature generated by the HMAC Sha256 algorithm.

To verify that requests were sent via the Toloka API:

  1. Make sure that all the necessary fields are specified in the header. Example:

    Toloka-Signature : {v=1, ts=946728000000, sign=d3...ab}
    
  2. Concatenate the values of the ts and v fields with the request payload string. Use a dot as the separator.

    946728000000.1.{"events":...}
    
  3. With the HMAC Sha256 algorithm, generate a signature from the concatenated string and your secret key.

    hmac_sha256(secret_key, '946728000000.1.{"events":...}')
    
  4. Compare the generated signature and the signature from the Toloka-Signature header. If they match, the requests were sent using the Toloka API.

Validation example

Let's take a look at a sample event notification sent by the Toloka API:

POST /webhook_endpoint HTTP/1.1
Host: client_api
Toloka-Signature: {v=1, ts=946728000000, sign=609af3eefd4c12b6afad30ab456efcd21fe82f4247d3340151a3ca0c97a6cbcb}
Content-Type: application/json
Content-Length: 355

{
  "events": [
    {
      "event_time": "2000-01-01T12:00:00",
      "project_id": "project-1",
      "pool_id": "pool-1",
      "uuid": "00000000-0000-0000-0000-000000000000",
      "task_suite_id": "task-suite-1",
      "assignment_id": "assignment-1",
      "webhook_subscription_id": "subscription-1",
      "type": "ASSIGNMENT_APPROVED"
    }
  ]
}

This incoming request is validated as follows:

import hmac
import hashlib

def is_valid_signature(secret_key, request_payload, toloka_header_ts,
    toloka_header_v, toloka_header_sign):
    data = str(toloka_header_ts) + '.' + str(toloka_header_v) + '.'
    + request_payload signature = hmac.new(bytearray(secret_key.encode()),
    bytearray(data.encode()), hashlib.sha256)
    return signature.hexdigest() == toloka_header_sign

# the secret key used when creating a subscription
secret_key = '12345'

# the body of the request received from the Toloka API
request_payload = "{\"events\":[{\"event_time\":
    \"2000-01-01T12:00:00\",
    \"project_id\":\"project-1\",
    \"pool_id\":\"pool-1\",
    \"uuid\":\"00000000-0000-0000-0000-000000000000\",
    \"task_suite_id\":\"task-suite-1\",
    \"assignment_id\":\"assignment-1\",
    \"webhook_subscription_id\":\"subscription-1\",
    \"type\":\"ASSIGNMENT_APPROVED\"}]}"

# the "ts" field value from the incoming Toloka-Signature header
toloka_header_ts = 946728000000

# the "v" field value from the incoming Toloka-Signature header
toloka_header_v = 1

# the "sign" field value from the incoming Toloka-Signature header
toloka_header_sign = '609af3eefd4c12b6afad30ab456efcd21fe82f4247d3340151a3ca0c97a6cbcb'

if (is_valid_signature(secret_key, request_payload, toloka_header_ts,
    toloka_header_v, toloka_header_sign)):
    print('Valid signature')
else:
    print('Invalid signature')