Communication Authentication
1. Introduction
There are several authentication methods available for use on the customer cloud server. This document provides an examplation of how the different methods work, along with code examples in PHP.
Note that the example code is intended for educational purposes; production code needs to perform additional validation and error checks to ensure maximum operational stability.
Releated Documentation
Device Communication Reference2. Authentication Overview
Messages from edge devices are sent to the IoT Server (over uTLS), and the IoT Server in turn sends the messages to the Custormer Cloud Server (over HTTPS):
Device -> IoT Server -> Cloud Server
Authentication data is sent with the HTTP request header.
2.1. Authorization Header
The IoT Server provides an Authorization header in the request sent to the Cloud Server. This header contains autentication data that should be verified by the cloud application.
Example authorization header:
Authorization: Bearer A6CD49E69D86ECAD1B3B63041CB70A89
2.2. Verification of Authorization Tokens
Note that the cloud server code must perform the authentication based on the Authorization header sent with the request. This is not done automatically!
2.3. Methods
Supported authentication methods are described below.
The authentication method is specified on the Settings Screen in the Management Console (it can also be set using the REST API).
2.4. Invalid Authentication
In case of invalid authentication, the cloud server should return a response with code 401 (UNAUTHORIZED).
3. Authentication Methods
The following sections describe the different authentication methods that are available.
3.1. None
When authentication None is set, no Authorization header is sent with the request.
Using no authentication is not recommended. Instead, use Bearer authentication, which is easy to specify in the Management Console and to verify on the cloud server.
3.2. Basic
Basic authentication uses a username and a password.
3.2.1. Format
The Basic authorization header has the following format:
Authorization: Basic <Base64-encoded-string>
The Base64-encoded-string contains the username and the password.
The username is URL encoded, followed by a colon separator and the password. This string is then Base64-encoded before it is sent to the cloud server.
Username and password format:
<URL-encoded-username>:<password>
Example:
Username: myusername
Password: mypassword
Unencoded authentication string:
myusername:mypassword
String encoded as Base64:
bXl1c2VybmFtZTpteXBhc3N3b3Jk
Example header:
Authorization: Basic bXl1c2VybmFtZTpteXBhc3N3b3Jk
3.2.2. Code Example
The following is an example of how to use Basic authentication on the server.
// Function that verifies username and password
function verify_basic($username, $password)
{
$success = false;
$headers = apache_request_headers();
if (array_key_exists("Authorization", $headers))
{
$auth = $headers["Authorization"];
$auth_parts = explode(" ", $auth, 2);
$method = $auth_parts[0];
$auth_data = base64_decode($auth_parts[1]);
$auth_data_parts = explode(":", $auth_data, 2);
$user = urldecode($auth_data_parts[0]);
$pass = $auth_data_parts[1];
$success = ($method === "Basic") && ($user === $username) && ($pass === $password);
}
return $success;
}
// Autheticate using the above function
$success = verify_basic("myusername", "mypassword");
if (!$success)
{
http_response_code(401);
exit();
}
3.3. Bearer
Bearer authentication (also called token authentication) is an authentication scheme that involves security tokens called bearer tokens. The name “Bearer authentication” can be understood as “give access to the bearer of this token.”
3.3.1. Format
The Bearer authorization header has the following format:
Authorization: Bearer <token>
Example header:
Authorization: Bearer A6CD49E69D86ECAD1B3B63041CB70A89
3.3.2. Code Example
The following is an example of how to use Bearer authentication on the server.
// Function that verifies the bearer token
function verify_bearer($token)
{
$success = false;
$headers = apache_request_headers();
if (array_key_exists("Authorization", $headers))
{
$auth = $headers["Authorization"];
$auth_parts = explode(" ", $auth, 2);
$success = ($auth_parts[0] === "Bearer") && ($auth_parts[1] === $token);
}
return $success;
}
// Autheticate using the above function
$success = verify_bearer("A6CD49E69D86ECAD1B3B63041CB70A89");
if (!$success)
{
http_response_code(401);
exit();
}
The Device Communication Tutorial contains a complete cloud server code example that uses Bearer authentication.
You can view this code example here:
Click to view server.php
<?php
/*
File: server.php
Receiving script on the integration server - accepts a PUT request with JSON data.
This code is part of the communication code example RIoTCounter.
TODO: You must edit the code below to use your bearer token.
*/
// TODO: Edit code to use your bearer token
define("BEARER_TOKEN", "YOUR-BEARER-TOKEN");
// Call main function
main();
function main()
{
// Get PUT data
$json = file_get_contents('php://input');
// Validate incoming request using Bearer authentication
$success = verify_bearer(BEARER_TOKEN);
if (!$success)
{
http_response_code(401);
exit();
}
// Log the data
$device_data = json_decode($json);
$device_data = process_data($device_data);
$result = log_data($device_data);
// Send response to device
$reponse_data = new stdClass();
if (false !== $result)
{
$reponse_data->Message = "OK"; // OK
}
else
{
$reponse_data->Message = "ER"; // ERROR
}
echo json_encode($reponse_data);
}
// Function that verifies the bearer token
function verify_bearer($token)
{
$success = false;
$headers = apache_request_headers();
if (array_key_exists("Authorization", $headers))
{
$auth = $headers["Authorization"];
$auth_parts = explode(" ", $auth, 2);
$success = ($auth_parts[0] === "Bearer") && ($auth_parts[1] === $token);
}
return $success;
}
// Process device data by adding and removing entries
function process_data($device_data)
{
// Remove entries of less interest for this application
unset($device_data->memory);
unset($device_data->hardware);
// Add server date and time
$device_data->date = date('y-m-d H:i:s');
// Add device id
$device_data->device_id = device_id_from_guid($device_data->guid);
return $device_data;
}
// JSON data is written to a log file. The format of the log file
// is a JSON array. The first element in the array is the most recent.
function log_data($device_data)
{
// Set destination file
$file_name = __DIR__ . "/data_counters.json";
// Existing data array
$log_data = [];
// Check if file exists
if (file_exists($file_name))
{
// Read file
$json = file_get_contents($file_name);
$log_data = json_decode($json);
}
// Add device data as the first element
array_unshift($log_data, $device_data);
// Maintain max array length
if (count($log_data) > 20)
{
array_pop($log_data);
}
// Write file
$json = json_encode($log_data, JSON_PRETTY_PRINT);
$result = file_put_contents($file_name, $json, LOCK_EX);
if (false === $result)
{
return false;
}
else
{
return true;
}
}
// Extract tenant id from the GUID
function tenant_id_from_guid($guid)
{
$bytes = str_split($guid, 2);
$byte_0 = hexdec($bytes[0]);
$byte_1 = hexdec($bytes[1]);
$R = 82; //ord("R");
$I = 73; //ord("I");
$tenant_id = (($byte_0 ^ $R) << 8) | ($byte_1 ^ $I);
return $tenant_id;
}
// Extract device id from the GUID
function device_id_from_guid($guid)
{
$bytes = str_split($guid, 2);
$byte_2 = hexdec($bytes[2]);
$byte_3 = hexdec($bytes[3]);
$o = 111; //ord("o");
$T = 84; //ord("T");
$device_id = (($byte_2 ^ $o) << 8) | ($byte_3 ^ $T);
return $device_id;
}
3.4. Digest
Digest authentication is based on a username and an md5 password hash, which is combined with a random “nonce” and an additional hash value.
3.4.1. Format
The Digest authorization HTTP header has the following format:
Digest username="<username>" nonce="<nonce>" authority="<digest>"
Example:
Digest username="myusername" nonce="66819CEC4FDCFA68F891465B968C592C" authority="658D1A7FACD989BCBF348D4FC29DB0D2"
To verify the digest hash, a computation is required.
Digest authentication verification involves the following parameters:
- Username: the name of the user as a plain unencoded string
- Passhash: an md5 hash of the user password
- Nonce: a combination of an 8 digit hexadecimal UTC timestamp and 24 random hexadecimal characters
- URL hash: a hash of the Integration URL
- Digest: a hash consisting of the passhash, the nonce, and the URL hash
The username, passhash, and URL hash are known by the server.
The authorization header contains the username, nonce and digest provided by the request.
Nonce
The Nonce has the following format:
32 chars
8 chars 24 chars
timestamp random hex value
Example nonce value:
66819CEC4FDCFA68F891465B968C592C
The timestamp is in the first 8 characters:
66819CEC
Which in decimal is:
1719770348
The random value is in the last 24 characters:
4FDCFA68F891465B968C592C
The nonce timestamp can be used for additional validation of the request.
URL Hash
The URL hash is computed as follows:
$url_hash = strtoupper(md5("PUT:" . $integration_url));
For example:
$integration_url = "https://mywebserver/server.php";
$url_hash = strtoupper(md5("PUT:" . $integration_url));
Digest
The digest is computed as follows:
$digest = strtoupper(md5($passhash . ":" . $nonce . ":" . $url_hash));
3.4.2. Verification
The request is verified as follows:
- the computed digest value is compared against the value of the authority parameter of the authorization header
- the server username is compared against the username provided by the request header.
See the code example below for futher details.
The nonce timestamp can be used for additional validation of the request.
3.4.3. Code Example
The following is an example of how to use Digest authentication on the server.
// Function that verifies username and password hash
function verify_digest($username, $passhash, $integration_url)
{
$success = false;
$headers = apache_request_headers();
if (array_key_exists("Authorization", $headers))
{
// Parse authorization header
$auth = $headers["Authorization"];
$auth_parts = explode(" ", $auth, 2);
$method = $auth_parts[0];
$param_parts = explode(" ", $auth_parts[1]);
$params = [];
foreach ($param_parts as $param)
{
$items = explode("=", $param);
$name = $items[0];
$value = trim($items[1], '"');
$params[$name] = $value;
}
// Validate
$url_hash = strtoupper(md5("PUT:" . $integration_url));
$digest = strtoupper(md5($passhash . ":" . $params["nonce"] . ":" . $url_hash));
$success = ($method === "Digest")
&& ($params["username"] === $username)
&& ($params["authority"] === $digest);
}
return $success;
}
// Autheticate using the above function
$success = verify_digest("myusername", "mypasshash", "https://mywebserver/server.php");
if (!$success)
{
http_response_code(401);
exit();
}