Signed Request

Publicly Authenticated Request

This method of request authentication is designed for Device to Server scenarios where it's imperative not to expose the Sinch Application Secret on end-user devices. As it is a weak authentication method, Verification will implement additional measures to verify the originator of the process.

Upon receiving such a request, Verification will issue a Verification Request Event to a CallbackURL configured in the App settings on the Dashboard. Failure to respond to this callback will result in rejection of the entire verification process. It's essential to correlate callbacks with your existing business processes to mitigate fraud risks.

Below is a pseudocode example illustrating how to set up an Authorization header in requests to Sinch.

Copy
Copied
    Authorization = "Application " + ApplicationKey

Application Signed Request

In production environments, we strongly recommend using more secure application signed requests over basic authentication.

The following pseudocode example and table demonstrates and explains how to sign a request for the Sinch platform. The result of this should be included in the HTTP Authorization header sent with the HTTP request.

Copy
Copied
    Content-MD5 = Base64 ( MD5 ( UTF8 ( [BODY] ) ) )

    Scheme = "Application"

    Signature = Base64 ( HMAC-SHA256 ( Base64-Decode ( ApplicationSecret ), UTF8 ( StringToSign ) ) );

    StringToSign = HTTP-Verb + "\n" +
                   Content-MD5 + "\n" +
                   Content-Type + "\n" +
                   CanonicalizedHeaders + "\n" +
                   CanonicalizedResource;

    Authorization = Scheme + " " + ApplicationKey + ":" + Signature
Pseudocode Component Description
CanonicalizedHeaders The only required header is x-timestamp.
CanonicalizedResource The path to the API resource. For example, verification/v1/verifications.
ApplicationKey The key for your Voice application found on your dashboard.
ApplicationSecret The secret for your Voice application found on your dashboard. Important!: The Application Secret value must be base64-decoded from before it's used for signing.

Example of an application signed request

For the following POST request to the protected resource /verification/v1/verifications,

Copy
Copied
    POST /verification/v1/verifications
    x-timestamp: 2014-06-04T13:41:58Z
    Content-Type: application/json

    {"identity": {"type": "number", "endpoint": "+46700000000"}, "method": "sms"}

the signature should be formed like this:

Copy
Copied
    Content-MD5 = Base64 ( MD5 ( UTF8 ( [BODY] ) ) )
        jANzQ+rgAHyf1MWQFSwvYw==

    StringToSign
    POST
    jANzQ+rgAHyf1MWQFSwvYw==
    application/json
    x-timestamp:2014-06-04T13:41:58Z
    /verification/v1/verifications

    Signature = Base64 ( HMAC-SHA256 ( Base64-Decode ( ApplicationSecret ), UTF8 ( StringToSign ) ) )
        qDXMwzfaxCRS849c/2R0hg0nphgdHciTo7OdM6MsdnM=

    HTTP Authorization Header Authorization: Application
        5F5C418A0F914BBC8234A9BF5EDDAD97:qDXMwzfaxCRS849c/2R0hg0nphgdHciTo7OdM6MsdnM=
Note

For requests that don’t contain a body (like GET requests) or requests where the body is empty, the Content-MD5 value of StringToSign should be left empty, as in the following example:

Copy
Copied
    StringToSign = HTTP-Verb + "\n" +
        "\n" +
        Content-Type + "\n" +
        CanonicalizedHeaders + "\n" +
        CanonicalizedResource;

Timestamp

The client must send a custom header x-timestamp (time) with each request that's validated by the server. This custom header is used to determine that the request is not too old. The timestamp is also part of the signature. The timestamp must be formatted to ISO 8061 specifications.

Important!

The timestamp must be in the Coordinated Universal Time (UTC) timezone.

Example

Copy
Copied
x-timestamp: 2014-06-02T15:39:31.2729234Z

Example implementations of application signing

Note:

Most of these examples require some setup steps before you can use them.

Node.jsJavaPythonPHP
Copy
Copied
const createHmac = require('create-hmac');
const crypto = require('crypto');
const hash = crypto.createHash('md5');
const utf8 = require('utf8');
const https =  require('https');
const util = require('util');

///////////////////////////////////////////////////////////////////////////////////////////
// Add key, secret, and endpoint from your chosen application details from dashboard.sinch.com
//////////////////////////////////////////////////////////////////////////////////////////
const application = {
    key: '', //key: The application key from the application you wish to use
    secret: '', //secret: The secret of the application you wish to use
    endpoint: '' //endpoint: The destination E164 formatted number that will receive the verification request
};

const bodyData = JSON.stringify({
  identity: {
      type: 'number',
      endpoint: application.endpoint
    },
    method: 'sms'
  }
});

let hmac = crypto.createHmac('sha256', Buffer.from(application.secret, 'base64'));
let contentMD5=hash.update(utf8.encode(bodyData)).digest('base64');
let contentLength = Buffer.byteLength(bodyData);
let timeStampISO = new Date().toISOString()

let stringToSign = 'POST' + '\n' +
                   contentMD5 + '\n' +
                   'application/json; charset=UTF-8' + '\n' +
                   'x-timestamp:'+ timeStampISO + '\n' +
                   '/verification/v1/verifications';

hmac.update(stringToSign);
let signature = hmac.digest('base64');

const options = {
  method: 'POST',
  hostname: '/verificationapi-v1.sinch.com',
  port: 443,
  path: '/verification/v1/verifications',
  headers: {
    'content-Type': 'application/json; charset=UTF-8',
    'x-timestamp': timeStampISO ,
    'content-length': contentLength,
    'authorization': String('application '+ application.key +':' + signature)
  },
  data: bodyData
}

console.log(util.inspect(options, false, null, true))

const req = https.request(options, (res) => {
  res.setEncoding('utf8');
  res.on('data', (chunk) => {
    console.log(`:: body response: => ${chunk}`);
  });
  res.on('end', () => {
    console.log(':: end of data in body response.');
  });
});

req.on('error', (e) => {
  console.error(`problem with request: ${e.message}`);
});

req.write(bodyData);
req.end();
Copy
Copied
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class App {

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, IOException, InterruptedException {
        var applicationKey = "";
        var applicationSecret = "";
        var b64DecodedApplicationSecret = Base64.getDecoder().decode(applicationSecret);

        var toNumber = "";

        var sinchVerificationUrl = "https://verification.api.sinch.com/verification/v1/verifications";

        var verificationRequest = """
            {
                "identity": {
                  "type": "number",
                  "endpoint": "%s"
                  },
                  "method": "sms"
                }
            }
            """.formatted(toNumber);
        
        byte[] encodedVerificationRequest = verificationRequest.getBytes(StandardCharsets.UTF_8);

        MessageDigest md = MessageDigest.getInstance("MD5");
        md.update(encodedVerificationRequest);
        var encodedMd5ToBase64VerificationRequest = Base64.getEncoder().encode(md.digest());
        
        var httpVerb = "POST";
        var requestContentType = "application/json; charset=UTF-8";
        var timeNow = ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT);
        var requestTimeStamp = "x-timestamp:" + timeNow;
        var requestUriPath = "/verification/v1/verifications";

        var stringToSign = String.join(System.lineSeparator()
            , httpVerb
            , new String(encodedMd5ToBase64VerificationRequest, StandardCharsets.UTF_8)
            , requestContentType
            , requestTimeStamp
            , requestUriPath
        );

        SecretKeySpec secretKeySpec = new SecretKeySpec(b64DecodedApplicationSecret, "HmacSHA256");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(secretKeySpec);
        byte[] hmacSha256 = mac.doFinal(stringToSign.getBytes());

        var authorizationSiganture = new String(Base64.getEncoder().encode(hmacSha256), StandardCharsets.UTF_8);

        var httpClient = HttpClient.newBuilder().build();

        var request = HttpRequest.newBuilder()
            .POST(HttpRequest.BodyPublishers.ofString(verificationRequest))
            .uri(URI.create(sinchVerificationUrl))
            .header("Content-Type", requestContentType)
            .header("Authorization", "Application " + applicationKey + ":" + authorizationSiganture)
            .header("x-timestamp", timeNow)
            .build();

        var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println(response.body());
    }
}
Copy
Copied
import hashlib
import hmac
import base64
from datetime import datetime, timezone
import requests
import json


application_key = ''
application_secret = ''
number_to_authenticate = ''

verification_request = {
    "identity": {
        "type": "number",
        "endpoint": number_to_authenticate
    },
    "method": "sms"
}


b64_encoded_application_secret = base64.b64decode(application_secret)
encoded_verification_request = json.dumps(verification_request).encode()
md5_verification_request = hashlib.md5(encoded_verification_request)
encoded_MD5_to_base64_verification_request = base64.b64encode(md5_verification_request.digest())

http_verb = 'POST'
request_content_type = 'application/json; charset=UTF-8'
time_now = datetime.now(timezone.utc).isoformat()
request_timestamp = "x-timestamp:" + time_now
request_uri_path = '/verification/v1/verifications'

string_to_sign = (
    http_verb + "\n"
    + encoded_MD5_to_base64_verification_request.decode() + "\n"
    + request_content_type + "\n"
    + request_timestamp + "\n"
    + request_uri_path
)

authorization_signature = base64.b64encode(
    hmac.new(b64_encoded_application_secret, string_to_sign.encode(), hashlib.sha256).digest()
).decode()

headers = {
    "Content-Type": request_content_type,
    "Authorization": (f"Application {application_key}:{authorization_signature}"),
    "x-timestamp": time_now
}

auth_response = requests.post(
    "https://verification.api.sinch.com/verification/v1/verifications",
    json=verification_request,
    headers=headers
)

print(auth_response.json())
Copy
Copied
<?php

declare(strict_types=1);

$applicationKey = "";
$applicationSecret= "";
$b64DecodedApplicationSecret = base64_decode($applicationSecret, true);

$toNumber = "";

$sinchVerificationUrl = "https://verification.api.sinch.com/verification/v1/verifications";

$verificationRequest = [
  "identity" => [
    "type" => "number",
    "endpoint" => $toNumber
    ],
    "method" => "sms"
  ]
];

$encodedVerificationRequest = utf8_encode(json_encode($verificationRequest, JSON_UNESCAPED_UNICODE));
$md5VerificationRequest = md5($encodedVerificationRequest, true);
$encodedMd5ToBase64VerificationRequest = base64_encode($md5VerificationRequest);

$httpVerb = 'POST';
$requestContentType = 'application/json; charset=UTF-8';
date_default_timezone_set('UTC');
$timeNow = date(DateTime::ATOM);
$requestTimeStamp = "x-timestamp:" . $timeNow;
$requestUriPath = "/verification/v1/verifications/";

$stringToSign = $httpVerb . "\n"
    . $encodedMd5ToBase64VerificationRequest . "\n"
    . $requestContentType . "\n"
    . $requestTimeStamp . "\n"
    . $requestUriPath;

$authorizationSignature = base64_encode(hash_hmac("sha256", $stringToSign, $b64DecodedApplicationSecret, true));

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_HTTPHEADER => [
    "Content-Type: {$requestContentType}",
    "x-timestamp: {$timeNow}",
    "Authorization: Application {$applicationKey}:{$authorizationSignature}"
  ],
  CURLOPT_POSTFIELDS => json_encode($verificationRequest),
  CURLOPT_URL => $sinchVerificationUrl,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => $httpVerb,
]);

$response = curl_exec($curl);
$error = curl_error($curl);

curl_close($curl);

echo $response;
We'd love to hear from you!
Rate this content:
Still have a question?
 
Ask the community.