npm package: @madaket/api-token-generator
Description: Madaket API, Javascript Token Generator
Author: Daniel Barrett
Latest version: 1.1.0

# Madaket Api Token Generator

## Quick Start
Install:
```bash
npm install @madaket/api-token-generator
```

Use:
```javascript
var TokenGenerator = require('@madaket/api-token-generator');
var auth_token = TokenGenerator.newAuthToken(api_key, api_secret);
```

All tokens generated using this method will expire in at least 10 minutes and at most 20 minutes.

## Example
To see a sample invocation, try it out from the commandline as follows.  In the root of the directory where you've installed this dependency (ie. where your `node_modules` lives):

```bash
node node_modules/@madaket/api-token-generator/example.js <api_key> <api_secret>
```

For example:
```bash
node example.js b1093d24-e174-418f-933b-e1ddb81cc2d0 k4pcumgykw25kyv2ux7k3dv6k52aqynuyfndq3dr
```

Don't have an API Key or Secret?  Please reach out to Madaket and we will provide you with them.


## Full Code

If you want to use the code on your own terms, you can copy it from here:
```javascript
var dateFormat = require('dateformat');
var CryptoJS = require('crypto-js');

var TokenGenerator = {
    newAuthToken: function (api_key, api_secret) {

        // (1) Get date in GMT and chop off trailing minute to create a 10-minute window
        var timestamp = TokenGenerator.getTenMinuteWindow();

        // (2) Concatenate Key, Secret, Timestamp window
        var cleartext = TokenGenerator.concatenateClearText(api_key, api_secret, timestamp);

        // (3) take SHA-256 of cleartext
        var byteData = CryptoJS.SHA256(cleartext);

        // (4) convert the resulting hex representation of those bytes to UTF-16 characters
        var stringData = TokenGenerator.convertBytesAsHexToString(byteData);

        // (5) get the resulting bytes of our new UTF-16 hex string (note that these aren't the same bytes that went into creating the string)
        var byteArray = TokenGenerator.getBytes(stringData);

        // (6) encode the resulting byte as Base64
        var base64Token = TokenGenerator.byteArrayToBase64(byteArray);

        // (7) encode the resulting base64 string as URI-safe
        return TokenGenerator.urlSafe(base64Token);
    },

    getTenMinuteWindow: function () {
        var now = Date.now();
        var fullDate = dateFormat(now, "GMT:yyyy-mm-dd-HH-MM");
        return fullDate.substring(0, fullDate.length - 1);
    },

    concatenateClearText: function (api_key, api_secret, timestamp) {
        return api_key + api_secret + timestamp;
    },

    convertBytesAsHexToString: function (sha256Bytes) {
        // javascript, when concatenating a string to a set of bytes will return the bytes' hexadecimal representation as UTF-16 characters (ie. a string)
        return sha256Bytes + '';
    },

    getBytes: function (str) {
        return str.split('').map(TokenGenerator.characterToByte);
    },

    characterToByte: function (x) {
        return x.charCodeAt(0);
    },

    urlSafe: function (base64String) {
        return base64String.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
    },

    byteArrayToBase64: function (arrayBuffer) {
        var base64 = '';
        var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

        var bytes = new Uint8Array(arrayBuffer);
        var byteLength = bytes.byteLength;
        var byteRemainder = byteLength % 3;
        var mainLength = byteLength - byteRemainder;

        var a, b, c, d;
        var chunk;

        // Main loop deals with bytes in chunks of 3
        for (var i = 0; i < mainLength; i = i + 3) {
            // Combine the three bytes into a single integer
            chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];

            // Use bitmasks to extract 6-bit segments from the triplet
            a = (chunk & 16515072) >> 18;// 16515072 = (2^6 - 1) << 18
            b = (chunk & 258048) >> 12;// 258048   = (2^6 - 1) << 12
            c = (chunk & 4032) >> 6;// 4032     = (2^6 - 1) << 6
            d = chunk & 63;// 63       = 2^6 - 1

            // Convert the raw binary segments to the appropriate ASCII encoding
            base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
        }

        // Deal with the remaining bytes and padding
        if (byteRemainder === 1) {
            chunk = bytes[mainLength];

            a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2

            // Set the 4 least significant bits to zero
            b = (chunk & 3) << 4; // 3   = 2^2 - 1

            base64 += encodings[a] + encodings[b] + '==';
        } else if (byteRemainder === 2) {
            chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];

            a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
            b = (chunk & 1008) >> 4; // 1008  = (2^6 - 1) << 4

            // Set the 2 least significant bits to zero
            c = (chunk & 15) << 2; // 15    = 2^4 - 1

            base64 += encodings[a] + encodings[b] + encodings[c] + '=';
        }

        return base64;
    }
};

module.exports = TokenGenerator;
```