3
\$\begingroup\$

I created an OAuth 1.0 signature generator using Ramda based on this.

I'm just getting to grips with it, so was wondering if someone more familiar with Ramda and functional programming would give me review.

const R = require('ramda')
const hmacsha1 = require('hmacsha1');

// For RFC 3986 Compliant URI Encoding.
let encode = R.pipe(
  encodeURIComponent,
  R.replace(/\!/g, '%21'),
  R.replace(/\'/g, '%27'),
  R.replace(/\(/g, '%28'),
  R.replace(/\)/g, '%29'),
  R.replace(/\*/g, '%2A')
);

// Create a random alphanumeric string of a given length.
let nonce = R.pipe(
  R.times(
    R.partial(
      chars => chars[Math.floor(Math.random() * chars.length)],
      [ '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ]
    )
  ),
  R.join('')
);

// Return the current timestamp in seconds
let now = R.pipe(
  Date.now,
  R.flip(R.divide)(1000),
  Math.floor
);

// Sort a list of pairs by the first item then the second.
let sortPairs = R.compose(
  R.sortBy(R.prop(0)),
  R.sortBy(R.prop(1))
);

// Encode a list of pairs.
let encodePairs = R.map(R.map(encode));

// Convert a list of pairs into a query string.
let toParams = R.compose(
  R.join('&'),
  R.map(R.join('='))
);

// Join key items with an ampersand and hash with given data.
let hash = R.pipe(
  R.join('&'),
  R.curry(R.binary(hmacsha1))
);

// Rename an objects keys
let renameKeys = R.curry((keysMap, obj) => {
  return R.reduce((acc, key) => {
    acc[keysMap[key] || key] = obj[key];
    return acc;
  }, {}, R.keys(obj));
});

// Convert arguments object to OAuth object.
let renameOAuthKeys = R.curry(renameKeys)({
  consumerKey: 'oauth_consumer_key',
  signatureMethod: 'oauth_signature_method',
  oauthToken: 'oauth_token',
  version: 'oauth_version',
});

// reverse concat
let fconcat = R.flip(R.concat);

// Prepend the method and url to the base string.
let prependRequest = R.pipe(
  R.adjust(R.toUpper, 0),
  R.adjust(R.compose(fconcat('&'), encode), 1),
  R.join('&'),
  R.concat
)

// Sort and encode the OAuth params.
let encodeParams = R.pipe(
  R.toPairs,
  encodePairs,
  sortPairs,
  toParams,
  encode
);

// Create dynamic OAuth values.
let addDynamicKeys = R.curry(nonceSize => R.merge({
  oauth_nonce: nonce(nonceSize),
  oauth_timestamp: now(),
}));

// Create an OAuth 1.0 signature.
module.exports = ({
  consumerKey,
  consumerSecret,
  oauthToken,
  oauthSecret,
  url,
  params={},
  data={},
  method='POST',
  version='1.0',
  signatureMethod='HMAC-SHA1',
  nonceSize=32
}) => R.pipe(
    R.merge(R.merge(params, data)),
    renameOAuthKeys,
    addDynamicKeys(nonceSize),
    encodeParams,
    prependRequest([method, url]),
    hash([consumerSecret, oauthSecret])
  )({ consumerKey, signatureMethod, oauthToken, version, })

Full Gist is here

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.