Skip to content

SolarNet API authentication scheme

Matt Magoffin edited this page Apr 12, 2020 · 16 revisions

Legacy Scheme

This document describes the V1 SolarNet authentication scheme. This has been superseded by the V2 scheme. The V1 scheme is still supported, but clients should migrate to the V2 scheme.

SolarNet authentication scheme

SolarNet uses a custom HTTP authentication scheme roughly similar to the Amazon S3 web API scheme. Clients of the API must use a security token pair to authenticate each request using a HMAC+SHA1 digest of specific request headers.

Request date

The request must include a HTTP date header, either using the standard HTTP Date header or the custom X-SN-Date header. If X-SN-Date is provided, it will be used in preference to the Date header. The exact date included in HTTP header is included in the message digest so its value must be known by the client. As many browser AJAX fameworks set the Date header automatically, the X-SN-Date can be easier to use.

The value of the request date must match the current date on SolarNetwork at the time the request is made, within a small tolerance value. If the difference between the HTTP header date and the SolarNetwork system date is too large, a HTTP 401 error will be returned with a message along the lines of date skew too large.

Authorization

The request must include a standard HTTP Authorization header using SolarNetworkWS as the authorization scheme. The authorization value is in the form token:hash where token is a SolarUser generated authorization token and hash is the HMAC+SHA1 hash generated from the token secret and message content (described in detail next), encoded as a Base64 string.

Digest message content

Each request must generate a message out of details included with the request; that message is then used as the input to the HMAC+SHA1 digest algorithm used to authenticate the request. The message consists of the following items, delimited by a newline character such that the last item does not have a newline character added to it (newline character is 0x0A, or \n in most programming languages):

  1. HTTP request method in upper-case, e.g. GET
  2. Value of Content-MD5 header (or empty string if none). The value can be either Base64 or hex encoded.
  3. Value of Content-Type header (or empty string if no content)
  4. Request date, from either a X-SN-Date header or a standard Date header, using the pattern EEE, dd MMM yyyy HH:mm:ss zzz in the GMT time zone (the zzz value must be the literal text GMT)
  5. Request path, including lexicographically sorted request parameters appended after a ? character and encoded as key=value and delimited by &. Note the keys and values are not URL encoded. The request parameters must be included regardless of the HTTP method if application/x-www-form-urlencoded encoding is used, for example, a POST request (see below).

Digest request path canonicalization

The request path component of the message digest contains the HTTP request path appended with any URL request parameters, sorted lexicographically by their keys. If the HTTP body is encoded as application/x-www-form-urlencoded, those parameters must be added to the path as well. For example, imagine a HTTP request such as:

POST /solaruser/api/v1/sec/instr/add HTTP/1.1
Host: data.solarnetwork.net
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 100

nodeId=11&topic=SetControlParameter&parameters%5B0%5D.name=/power/switch/1&parameters%5B0%5D.value=1

For the message request path, the POST parameters would be sorted into

  1. nodeId=1
  2. parameters[0].name=/power/switch/1
  3. parameters[0].value=1
  4. topic=SetControlParameter

and appended to the path like this:

/solaruser/api/v1/sec/instr/add?nodeId=11&parameters[0].name=/power/switch/1&parameters[0].value=1&topic=SetControlParameter

Putting it all together

Here is an example of a GET request using the SolarNetworkWS authentication scheme:

GET /solaruser/api/v1/sec/instr/viewActive?nodeId=11 HTTP/1.1
Host: data.solarnetwork.net
X-SN-Date: Mon, 23 Sep 2013 03:39:39 GMT
Authorization: SolarNetworkWS a09sjds09wu9wjsd9uy2:IXeexIsjH7beMhxLPU1//1fM9FA=

The raw value of the message content is below (note how lines #2 and #3 are empty):

GET


Mon, 23 Sep 2013 03:39:39 GMT
/solaruser/api/v1/sec/instr/viewActive?nodeId=11

Assuming the message is stored in a variable msg then the Authorization header would be calculated like this, using pseudo code:

token = "a09sjds09wu9wjsd9uy2";          // the SolarNet auth token
secret = "my token secret";              // the secret associated with the SolarNet auth token
msg = "...";                             // calculated via rules above
hash = Base64(HmacSHA1(msg, secret));    // call function to hash message, then Base64 encode
authHeader = "Authorization: SolarNetworkWS " +token + ":" + hash;

API Sampler

See https://go.solarnetwork.net/dev/api/ for a jQuery client implementation of the SolarNetworkWS authentication scheme designed to showcase how the API can be used.

Sample code

Here is some examples on using the SolarNetworkWS authentication scheme. Note that the snippets are greatly simplified, and don't handle the canonicalization rules for arbitrary requests. Notably, the query strings are sorted already, and don't require any URI escaping.

JavaScript with jQuery and CryptoJS

This example shows JavaScript using jQuery and CryptoJS to query data:

// auth tokens provided by user somewhere...
var token = "abc123";
var secret = "def456";

// gather request details
var now = new Date().toUTCString();
var path = "/solarquery/api/v1/sec/datum/query";
var query = "?endDate=2014-02-08&nodeId=1&startDate=2014-02-01&type=Consumption";

// construct the message to digest
var msg = "GET\n";
msg += "\n";
msg += "\n";
msg += now + "\n";
msg += path + query;

// create HMAC+SHA1 digest
var hash = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA1(msg, secret));

$.ajax({
	type: 'GET',
	url: "https://data.solarnetwork.net" + path + query,
	dataType: 'json',
	beforeSend: function(xhr) {
		// set the headers on our request
		xhr.setRequestHeader('X-SN-Date', now);
		xhr.setRequestHeader('Authorization', 'SolarNetworkWS ' + token + ":" + hash);
	}
}).done(function (data) {
	// do something with data object
});

PHP with cURL

This example shows a PHP script that uses curl to query data:

// auth tokens provided by user somewhere...
$token = "abc123";
$secret = "def456";

// gather request details
$now = gmdate('D, d M Y H:i:s \G\M\T');
$path = "/solarquery/api/v1/sec/datum/query";
$query = "?endDate=2014-02-08&nodeId=1&startDate=2014-02-01&type=Consumption";

// construct the message to digest
$msg = "GET\n";
$msg .= "\n";
$msg .= "\n";
$msg .= $now . "\n";
$msg .= $path . $query;

// create HMAC+SHA1 digest
$hash = base64_encode(hash_hmac('sha1', $msg, $secret, true));

$httpHeaders = array(
	"X-SN-Date: " . now,
	"Authorization SolarNetworkWS " . $token . ":" . $hash
);

// execute request using curl
$curlOpts = array(
	CURLOPT_URL        => "https://data.solarnetwork.net" . $path . $query,
	CURLOPT_HTTPHEADER => httpHeaders
);

$curl = curl_init();
curl_setopt_array($curl, $opts);
$data = curl_exec($curl);
curl_close($curl);
Clone this wiki locally