# Application Signed Request This is secure, HMAC based authentication method for interfacing the Verification API. In production environments, we strongly recommend using it over basic authentication. Note: [SDKs](/docs/verification/sdk) implement Application Signed Request. Use them to streamline the authentication process in your app. 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. ```shell 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`](#timestamp). | | `CanonicalizedResource` | The path to the API resource. For example, `verification/v1/verifications`. | | `ApplicationKey` | The key for your Voice application found on your [dashboard](https://dashboard.sinch.com/verification/apps). | | `ApplicationSecret` | The secret for your Voice application found on your [dashboard](https://dashboard.sinch.com/verification/apps). 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, ```shell 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: ```shell 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: ```shell 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](http://en.wikipedia.org/wiki/ISO_8601) specifications. Important! The timestamp must be in the Coordinated Universal Time (UTC) timezone. ### Example ```shell 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. ```javascript 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(); ``` ```java 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()); } } ``` ```python 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()) ``` ```php [ "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; ```