Generating SAS Tokens

This page includes a step-by-step explanation of the SAS token generation algorithm and sample code for servers in different languages. You don’t need to have a detailed understanding of the algorithm to be able to use the sample code and start using the Plans Static API.

Additionally, we provide an online SAS token generator that you can use during development to validate your generation code, or to help you throw something together quickly.

Generation Algorithm

  1. Generate the correct string to sign based on parameters. This string has the following structure and order of parameters:

    <floor/campus/plan code>[:<icons>][:<layers>]:<partner code>:<expiry>
    

    The floor/campus/plan code, partner code, and expiry time are mandatory. If you provide values for the icons or layers parameters, you must include them in the string to sign. This is true even if you provide an empty list (by passing &icons= or &layers=). If you do not provide values for these parameters, you must not include anything for them in the string to sign.

    Examples

    • partner=ptnr_cadr0g675rbk0fv03fm5fewz7&floor=flr_95kpvk552x7ue5xvb4f290a4q&expiry=2145916800 &w=640&h=640&format=png
      becomes flr_95kpvk552x7ue5xvb4f290a4q:ptnr_cadr0g675rbk0fv03fm5fewz7:2145916800

    • partner=ptnr_cadr0g675rbk0fv03fm5fewz7&plan=pln_gqfz7uu59qze049ro3uxyk8t6&expiry=2145916800 &w=640&h=640&format=png
      becomes pln_gqfz7uu59qze049ro3uxyk8t6:ptnr_cadr0g675rbk0fv03fm5fewz7:2145916800

    • partner=ptnr_cadr0g675rbk0fv03fm5fewz7&floor=flr_95kpvk552x7ue5xvb4f290a4q&expiry=2145916800 &w=640&h=640&format=png&layers=structure,interiorZone,leaderLineIcon
      becomes flr_95kpvk552x7ue5xvb4f290a4q:structure,interiorZone,leaderLineIcon:ptnr_cadr0g675rbk0fv03fm5fewz7:2145916800

    • partner=ptnr_cadr0g675rbk0fv03fm5fewz7&campus=camp_v03fm5fewz75xvb4f290a4q&expiry=2145916800 &w=640&h=640&format=png&icons=
      becomes camp_v03fm5fewz75xvb4f290a4q::ptnr_cadr0g675rbk0fv03fm5fewz7:2145916800. Note that the empty icons list is still included in the string to sign.

    • partner=ptnr_cadr0g675rbk0fv03fm5fewz7&floor=flr_95kpvk552x7ue5xvb4f290a4q&expiry=2145916800 &w=640&h=640&format=png&layers=interiorZone,leaderLineIcon&icons=mcp,hyd
      becomes flr_95kpvk552x7ue5xvb4f290a4q:mcp,hyd:interiorZone,leaderLineIcon:ptnr_cadr0g675rbk0fv03fm5fewz7:2145916800

  2. Ensure the string is UTF-8 encoded. Signing algorithms work on bytes, so it’s important that the string is represented with the correct byte encoding.

  3. Generate an HMAC, using the SHA256 hashing algorithm and your UTF-8 encoded API secret as the key.

  4. Create a Base64 digest of that HMAC.

  5. URL-encode the Base64 digest. It doesn’t matter whether your URL encoding uses uppercase or lowercase letters when escaping characters (e.g turning / into %2F vs %2f).

  6. Pass that URL-encoded string to the Plans Static API, along with the API key that’s associated with your API secret. (Our servers need to figure out whose secret was used to generate the HMAC so that they can validate it.)

Sample Code

C#

using System;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Web;

public class SasTokenGenerator
{
    private static string API_SECRET = "YOUR_API_SECRET";
  
    public static string Generate(
        string floorOrCampusOrPlanCode,
        string icons, // pass null if you aren't using the `icons` parameter
        string layers, // pass null if you aren't using the `layers` parameter
        string partnerCode,
        long expiry
    )	{
        // 1. Generate the correct string to sign.
        var parts = new []{
            floorOrCampusOrPlanCode,
            icons,
            layers,
            partnerCode,
            expiry.ToString()
        }.Where(p => p != null);
        var stringToSign = string.Join(":", parts);
        
        // 2. Ensure the string is UTF-8 encoded.
        var stringToSignUTF8 = Encoding.UTF8.GetBytes(stringToSign);
        
        // 3. Generate an HMAC, using your UTF-8 encoded API secret as the key.
        HMACSHA256 algorithm = new HMACSHA256(Encoding.UTF8.GetBytes(API_SECRET));
        var digestBytes = algorithm.ComputeHash(stringToSignUTF8);
        
        // 4. Create a Base64 digest of that HMAC.
        var base64Digest = Convert.ToBase64String(digestBytes);
      
        // 5. URL-encode the Base64 digest.
        var sasToken = HttpUtility.UrlEncode(base64Digest);
        
        return sasToken;
    }
}

Node.js

const crypto = require('crypto');
const API_SECRET = 'YOUR_API_SECRET';

function generateSASToken (
    floorOrCampusOrPlanCode, // string
    icons, // string | null, pass null if you aren't using the `icons` parameter
    layers, // string | null, pass null if you aren't using the `layers` parameter
    partnerCode, // string
    expiry // number
) {
    // 1. Generate the correct string to sign.
    var parts = [
        floorOrCampusOrPlanCode,
        icons,
        layers,
        partnerCode,
        expiry.toString()
    ].filter(x => x != null);
    var stringToSign = parts.join(':');

    // 2. Ensure the string is UTF-8 encoded.
    var stringToSignUTF8 = Buffer.from(stringToSign, 'utf8');

    // 3. Generate an HMAC, using your UTF-8 encoded API secret as the key.
    var algorithm = crypto.createHmac('sha256', Buffer.from(API_SECRET, 'utf8'));
    algorithm.update(stringToSignUTF8);
    
    // 4. Create a Base64 digest of that HMAC.
    var base64Digest = algorithm.digest('base64');

    // 5. URL-encode the Base64 digest.
    var sasToken = encodeURIComponent(base64Digest);

    return sasToken;
}

Python 3

import base64
import datetime
import hashlib
import hmac
import json
import urllib.parse

API_SECRET = 'YOUR_API_SECRET'

def generate_sas_token(partner_code, campus_or_floor_code, expiry, icons=None, layers=None):
    # 1. Generate the correct string to sign.
    params = [campus_or_floor_code, icons, layers, partner_code, expiry]
    string_to_sign = ":".join([p for p in params if p is not None])

    # 2. Ensure the string is UTF-8 encoded.
    string_to_sign_utf8 = string_to_sign.encode('utf-8')

    # 3. Generate an HMAC, using your UTF-8 encoded API secret as the key.
    sig = hmac.new(bytes(API_SECRET, 'latin-1'), msg=bytes(string_to_sign, 'latin-1'), digestmod=hashlib.sha256).digest()

    # 4. Create a Base64 digest of that HMAC.
    base64_digest = base64.b64encode(sig).decode('utf8')

    # 5. URL-encode the Base64 digest.
    sas_token = urllib.parse.quote(base64_digest)
    
    return sas_token

PHP

define('API_SECRET', 'YOUR_API_SECRET');

/**
  * Generate a Locatrix SAS token
  * @param string $floorOrCampusCode Floor code or campus code
  * @param string|null $icons Pass null if you aren't using the `icons` parameter
  * @param string $layers Pass null if you aren't using the `layers` parameter
  * @param string $partnerCode Partner code
  * @param int $expiry Unix timestamp expiry date for the token
  * @return 
  */
function generateSASToken($floorOrCampusCode, $icons, $layers, $partnerCode, $expiry) {
    // 1. Generate the correct string to sign.
    $parts = array_filter([
        $floorOrCampusCode,
        $icons,
        $layers,
        $partnerCode,
        $expiry
    ]);
    
    $stringToSign = implode(':', $parts);
    
    // 2. Ensure the string is UTF-8 encoded.
    $stringToSignUTF8 = utf8_encode($stringToSign);

    // 3. Generate an HMAC, using your UTF-8 encoded API secret as the key.
    $hmac = hash_hmac('sha256', $stringToSignUTF8, API_SECRET, true);
    
    // 4. Create a Base64 digest of that HMAC.
    $base64Digest = base64_encode($hmac);

    // 5. URL-encode the Base64 digest.
    $sasToken = urlencode($base64Digest);

    return $sasToken;
}