Tuesday 28 July 2015

OAuth1.0a node.js signature and utc timestamp

Got chance to work on OAuth1.0a creating signature with nonce and timestamp.

OAuth-1.0a.js file for correct signature creation and timestamp should be in UTC. Sharing my code.

if (typeof (module!== 'undefined' && typeof (exports!== 'undefined'{
    module.exports = OAuth;
    var CryptoJS = require("crypto-js");
}

/**
 * Constructor
 * @param {Object} opts consumer key and secret
 */
function OAuth(opts) {
    if (!(this instanceof OAuth)) {
        return new OAuth(opts);
    }

    if (!opts{
        opts = {};
    }

    if (!opts.consumer{
        throw new Error('consumer option is required');
    }

    this.consumer = opts.consumer;
    this.signature_method = opts.signature_method || 'HMAC-SHA1';
    this.nonce_length = opts.nonce_length || 32;
    this.version = opts.version || '1.0';
    this.parameter_seperator = opts.parameter_seperator || '';

    if (typeof opts.last_ampersand === 'undefined'{
        this.last_ampersand = true;
    } else {
        this.last_ampersand = opts.last_ampersand;
    }

    switch (this.signature_method{
        case 'HMAC-SHA1':
            this.hash = function (base_string, key) {
                return CryptoJS.HmacSHA1(base_string, key).toString(CryptoJS.enc.Base64);
            };
            break;

        case 'HMAC-SHA256':
            this.hash = function (base_string, key) {
                return CryptoJS.HmacSHA256(base_string, key).toString(CryptoJS.enc.Base64);
            };
            break;

        case 'PLAINTEXT':
            this.hash = function (base_string, key) {
                return key;
            };
            break;

        case 'RSA-SHA1':
            throw new Error('oauth-1.0a does not support this signature method right now. Coming Soon...');
        default:
            throw new Error('The OAuth 1.0a protocol defines three signature methods: HMAC-SHA1, RSA-SHA1, and PLAINTEXT only');
    }
}

/**
 * OAuth request authorize
 * @param  {Object} request data
 * {
 *     method,
 *     url,
 *     data
 * }
 * @param  {Object} public and secret token
 * @return {Object} OAuth Authorized data
 */
OAuth.prototype.authorize = function (request, token) {
    var oauth_data = {
        oauth_callback: 'about:blank', //    added by sourabh
        oauth_consumer_key: this.consumer.public,
        oauth_nonce: this.getNonce(),
        oauth_signature_method: this.signature_method,
        oauth_timestamp: this.getTimeStamp(),
        oauth_version: this.version
    };

    if (!token{
        token = {};
    }

    if (token.public{
        //oauth_data.oauth_token = token.public;    //    commented by sourabh
    }

    if (!request.data{
        request.data = {};
    }

    oauth_data.oauth_signature = this.getSignature(request, token.secret, oauth_data);

    return oauth_data;
};

/**
 * Create a OAuth Signature
 * @param  {Object} request data
 * @param  {Object} token_secret public and secret token
 * @param  {Object} oauth_data   OAuth data
 * @return {String} Signature
 */
OAuth.prototype.getSignature = function (request, token_secret, oauth_data) {
    return this.hash(this.getBaseString(request, oauth_data), this.getSigningKey(token_secret));
};

/**
 * Base String = Method + Base Url + ParameterString
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {String} Base String
 */
OAuth.prototype.getBaseString = function (request, oauth_data) {
    return request.method.toUpperCase() + '&' + this.percentEncode(this.getBaseUrl(request.url)) + '&' + this.percentEncode(this.getParameterString(request, oauth_data));
};

/**
 * Get data from url
 * -> merge with oauth data
 * -> percent encode key & value
 * -> sort
 *
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {Object} Parameter string data
 */
OAuth.prototype.getParameterString = function (request, oauth_data) {
    var base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.mergeObject(request.data, this.deParamUrl(request.url)))));

    var data_str = '';

    //base_string_data to string
    for (var key in base_string_data{
        data_str += key + '=' + base_string_data[key+ '&';
    }

    //remove the last character
    data_str = data_str.substr(0, data_str.length - 1);
    return data_str;
};

/**
 * Create a Signing Key
 * @param  {String} token_secret Secret Token
 * @return {String} Signing Key
 */
OAuth.prototype.getSigningKey = function (token_secret) {
    token_secret = token_secret || '';

    if (!this.last_ampersand && !token_secret{
        return this.percentEncode(this.consumer.secret);
    }
    // commented by Sourabh
    //return this.percentEncode(this.consumer.secret) + '&' + this.percentEncode(token_secret);
    return this.percentEncode(this.consumer.secret+ '&';
};

/**
 * Get base url
 * @param  {String} url
 * @return {String}
 */
OAuth.prototype.getBaseUrl = function (url) {
    return url.split('?')[0];
};

/**
 * Get data from String
 * @param  {String} string
 * @return {Object}
 */
OAuth.prototype.deParam = function (string) {
    var arr = decodeURIComponent(string).split('&');
    var data = {};

    for (var i = 0; i < arr.length; i++{
        var item = arr[i].split('=');
        data[item[0]] = item[1];
    }
    return data;
};

/**
 * Get data from url
 * @param  {String} url
 * @return {Object}
 */
OAuth.prototype.deParamUrl = function (url) {
    var tmp = url.split('?');

    if (tmp.length === 1)
        return {};

    return this.deParam(tmp[1]);
};

/**
 * Percent Encode
 * @param  {String} str
 * @return {String} percent encoded string
 */
OAuth.prototype.percentEncode = function (str) {
    return encodeURIComponent(str)
        .replace(/\!/g, "%21")
        .replace(/\*/g, "%2A")
        .replace(/\'/g, "%27")
        .replace(/\(/g, "%28")
        .replace(/\)/g, "%29");
};

/**
 * Percent Encode Object
 * @param  {Object} data
 * @return {Object} percent encoded data
 */
OAuth.prototype.percentEncodeData = function (data) {
    var result = {};

    for (var key in data{
        result[this.percentEncode(key)] = this.percentEncode(data[key]);
    }

    return result;
};

/**
 * Get OAuth data as Header
 * @param  {Object} oauth_data
 * @return {String} Header data key - value
 */
OAuth.prototype.toHeader = function (oauth_data) {
    oauth_data = this.sortObject(oauth_data);

    var header_value = 'OAuth ';

    for (var key in oauth_data{
        if (key.indexOf('oauth_'=== -1)
            continue;
        header_value += this.percentEncode(key+ '="' + this.percentEncode(oauth_data[key]) + '"' + this.parameter_seperator;
    }

    return {
        Authorization: header_value.substr(0, header_value.length - this.parameter_seperator.length//cut the last chars
    };
};

/**
 * Create a random word characters string with input length
 * @return {String} a random word characters string
 */
OAuth.prototype.getNonce = function () {
    var word_characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    var result = '';

    for (var i = 0; i < this.nonce_length; i++{
        result += word_characters[parseInt(Math.random() * word_characters.length, 10)];
    }

    return result;
};

/**
 * Get Current Unix TimeStamp
 * @return {Int} current unix timestamp
 */
OAuth.prototype.getTimeStamp = function () {
    //return parseInt(new Date().getTime()/1000, 10);
    // changed by sourabh
    var now = new Date;
    return utc_timestamp = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(),
        now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds(), now.getUTCMilliseconds());
};

////////////////////// HELPER FUNCTIONS //////////////////////

/**
 * Merge object
 * @param  {Object} obj1
 * @param  {Object} obj2
 * @return {Object}
 */
OAuth.prototype.mergeObject = function (obj1, obj2) {
    var merged_obj = obj1;
    for (var key in obj2{
        merged_obj[key= obj2[key];
    }
    return merged_obj;
};

/**
 * Sort object by key
 * @param  {Object} data
 * @return {Object} sorted object
 */
OAuth.prototype.sortObject = function (data) {
    var keys = Object.keys(data);
    var result = {};

    keys.sort();

    for (var i = 0; i < keys.length; i++{
        var key = keys[i];
        result[key= data[key];
    }

    return result;
};

Create .ICS file using PHP code

Recently worked on creating a .ics file in PHP after a very long time, code so thought to share with everybody. Please find below the comple...