Communication Authentication

Customer cloud server authentication methods

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.

2. 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.

Basic authentication

// 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.

Bearer authentication

// 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
File: communication/RIoTCounter/cloud/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.

Digest authentication

// 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();
}