UNPKG

node-oauth-1.0a-ts

Version:

OAuth 1.0a Request Authorization for Node and Browser.

355 lines 18.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const randomstring = require("randomstring"); const crypto = require("crypto"); const signer_1 = require("./signer"); const utils_1 = require("./utils"); /** * OAuth 1.0a signature generator * * @example <caption>Setup</caption> * let OAuth = require('node-oauth-1.0a'); * let request = require('request'); * * let oauth = new OAuth({ * consumer: { * public: '<consumer key>', * secret: '<consumer secret>', * } * }); * let request_data = { * url: 'https://api.twitter.com/1/statuses/update.json?include_entities=true', * method: 'POST', * data: { * status: 'Hello Ladies + Gentlemen, a signed OAuth request!' * } * }; * let token = { * public: '370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb', * secret: 'LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE' * }; * * @example <caption>Sending in POST body with request library</caption> * let formData = Object.assign( * {}, * request_data.data, * oauth.authorize(request_data, token) * ); * request({ * url: request_data.url, * method: request_data.method, * form: oauth.buildQueryString(formData) * }, function(error, response, body) { * // Process data * }); * * @example <caption>Sending in Authorization header with request library</caption> * request({ * url: request_data.url, * method: request_data.method, * form: oauth.buildQueryString(request_data.data), * headers: { * Authorization: oauth.getHeader(request_data, token) * } * }, function(error, response, body) { * // Process data * }); * */ class OAuth { /** * @param {Object} opts * @param {Object} opts.consumer Consumer token (required) * @param {string} opts.consumer.public Consumer key (public key) * @param {string} opts.consumer.private Consumer secret * @param {number} [opts.nonce_length=32] Length of nonce (oauth_nonce) * @param {string} [opts.signature_method="HMAC-SHA1"] Signing algorithm * Supported algorithms: * - `HMAC-SHA1` * - `PLAINTEXT` * - `HMAC-SHA256` * * Note that `HMAC-256` is non-standard. * @param {string} [opts.version=1.0] OAuth version (oauth_version) * @param {boolean} [opts.last_ampersand=true] Whether to append trailing * ampersand to signing key */ constructor(opts = {}) { if (!opts.consumer) { throw new Error('consumer option is required'); } this._opts = Object.assign({ nonce_length: 32, signature_method: 'HMAC-SHA1', version: '1.0', last_ampersand: true, parameter_separator: ', ', }, opts, { consumer: opts.consumer }); } /** * Sign a string with key * @private * @param {string} str String to sign * @param {string} key HMAC key * @return {string} Signed string in base64 format */ _sign(str, key) { if (!this._signer) { // Cache the signer this._signer = this._getSigner(this._opts.signature_method); } return this._signer(str, key); } /** * Retrieve a signing algorithm by algorithm name * @private * @param {SignerType} type Algorithm name * @throws {Error} Algorithm is not supported * @return {Function} Algorithm implementation */ _getSigner(type) { if (signer_1.Signer[type]) { return signer_1.Signer[type]; } else { let supported = JSON.stringify(Object.keys(signer_1.Signer)); throw new Error(`Hash type ${type} not supported. Supported: ${supported}`); } } /** * Create OAuth signing data for attaching to request body * @param {Object} request * @param {string} request.method HTTP Method name (eg. `GET`, `POST`, `PUT`) * @param {string} request.url URL * @param {Object} request.data Post data as a key, value map * * @param {Object} [token={}] User token * @param {string} [token.key] Token public key * @param {string} [token.secret] Token secret key * * @return {Object} OAuth signing data. You probably want to put this in your POST body * * @example * let request = { * method: 'POST', * url: 'https://api.twitter.com/1.1/statuses/update.json', * data: { * status: 'Hello, world!' * } * }; * let token = { * public: '<user token>', * private: '<user token secret>' * }; * let oauth_data = oauth.authorize(request, token); * console.log(oauth_data); * * @example <caption>Example response</caption> * { * "oauth_consumer_key": "xvz1evFS4wEEPTGEFPHBog", * "oauth_nonce": "kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg", * "oauth_signature_method": "HMAC-SHA1", * "oauth_timestamp": 1318622958, * "oauth_version": "1.0", * "oauth_token": "370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb", * "oauth_signature": "tnnArxj06cWHq44gCs1OSKk/jLY=" * } */ authorize(request, token) { let bodyToHash; if (request.includeBodyHash) { if (typeof request.data != 'string') { throw Error("When using includeBodyHash request.data must be the exact data string of the request"); } bodyToHash = request.data; } token = token || {}; bodyToHash = bodyToHash || undefined; let oauth_data = this._getOAuthData(token, bodyToHash); oauth_data.oauth_signature = this.getSignature(request, token.secret, oauth_data); return oauth_data; } /** * Create OAuth Authorization header * @param {Object} request * @param {string} request.method HTTP Method name (eg. `GET`, `POST`, `PUT`) * @param {string} request.url URL * @param {Object} request.data Post data as a key, value map * * @param {Object} [token={}] User token * @param {string} [token.key] Token public key * @param {string} [token.secret] Token secret key * * @return {string} Authorization header value */ getHeader(request, token) { let oauth_data = this.authorize(request, token); return utils_1.default.toHeader(oauth_data, this._opts.parameter_separator); } /** * Format OAuth signing data for sending via HTTP Header * @param {Object} oauth_data OAuth signing data as returned from * {@link OAuth#authorize} * @return {Object} Headers required to sign the request * @deprecated This method is preserved for backward compatibility with * oauth-1.0a. New implementors should use {@link OAuth#getHeader} instead. */ toHeader(oauth_data) { return { Authorization: utils_1.default.toHeader(oauth_data, this._opts.parameter_separator) }; } /** * Create oauth_signature from request. Usually you probably want to use * {@link OAuth#authorize} instead. * @param {Object} request * @param {string} request.method HTTP Method name (eg. `GET`, `POST`, `PUT`) * @param {string} request.url URL * @param {Object} request.data Post data as a key, value map * * @param {string} token signing token * * @param {Object} oauth_data * @param {string} oauth_data.oauth_consumer_key Consumer key * @param {string} oauth_data.oauth_nonce Nonce string * @param {string} oauth_data.oauth_signature_method Signing algorithm name * (only for building signing string, the actual signing algorithm is set by * class {@link OAuth#constructor} arguments) * @param {number} oauth_data.oauth_timestamp Current time in seconds * @param {string} oauth_data.oauth_version OAuth version (should be 1.0) * * @return {string} Value of oauth_signature field */ getSignature(request, token, oauth_data) { let baseString = this._getBaseString(request, oauth_data); return this._sign(baseString, this._getSigningKey(token)); } /** * Create new OAuth data * @private * @param {Object} [token] User token * @param {string} token.public Token public key * @param {string|undefined} bodyToHash the body of the rquest (after decompression) as a string * @return {Object} OAuth data without oauth_signature * * @example <caption>Example response</caption> * { * "oauth_consumer_key": "xvz1evFS4wEEPTGEFPHBog", * "oauth_nonce": "kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg", * "oauth_signature_method": "HMAC-SHA1", * "oauth_timestamp": 1318622958, * "oauth_version": "1.0", * "oauth_token": "370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb", * } */ _getOAuthData(token, bodyToHash) { let oauth_data = { oauth_consumer_key: this._opts.consumer.public, oauth_nonce: this._getNonce(), oauth_signature_method: this._opts.signature_method, oauth_timestamp: this._getTimeStamp(), oauth_version: this._opts.version, }; if (token && token.public) { oauth_data.oauth_token = token.public; } if (bodyToHash) { oauth_data.oauth_body_hash = this._getBodyHash(bodyToHash); } return oauth_data; } /** * Get the body hash for the request according to * [OAuth Request Body Hash]{@link http://web.archive.org/web/20160413130001/https://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html} // tslint:disable-line * @param {Object} [token] User token * @param {string} token.secret Token secret key * @param {string} bodyToHash the body of the rquest (after decompression) as a string * @return {string} the hash of the body, using the same algorithm as for signing the request */ _getBodyHash(bodyToHash) { return crypto.createHash('sha1').update(bodyToHash).digest('base64'); } /** * Build authorization string to sign. * * An authorization string composes of HTTP method name, base URL and * parameters (both request parameters and OAuth data) * @private * @param {Object} request Request object * @param {Object} oauth_data OAuth parameters * @return {string} Authorization string */ _getBaseString(request, oauth_data) { let out = [ request.method.toUpperCase(), utils_1.default.getBaseUrl(request.url), utils_1.default.getParameterString(request, oauth_data) ]; return out.map(item => utils_1.default.percentEncode(item)) .join('&'); } /** * Build a query string similar to {@link querystring.encode}, but * escape things correctly per OAuth spec * * @param {Object} data Object to encode as query string * @return {string} Query string object */ buildQueryString(data) { data = utils_1.default.toSortedMap(data); return utils_1.default.stringifyQueryMap(data, '&', '=', { encodeURIComponent: utils_1.default.percentEncode }); } /** * Build signing key. * * A signing key composes of consumer secret and, * optionally, user token secret. * * This method will append trailing ampersand when `token_secret` is unset * if `last_ampersand` constructor option is set. * * @private * @param {string} [token_secret] User token secret * @return {string} Signing Key */ _getSigningKey(token_secret) { let out = [ this._opts.consumer.secret ]; if (this._opts.last_ampersand || token_secret) { out.push(token_secret || ''); } return out.map(item => utils_1.default.percentEncode(item)) .join('&'); } /** * Create nonce. * * Nonce is a random string to prevent replaying of requests. * * Default nonce length is 32 characters, and can be specified by * `nonce_length` constructor option. * * @private * @return {string} Nonce string */ _getNonce() { return randomstring.generate({ length: this._opts.nonce_length || 32, charset: 'alphanumeric' }); } /** * Create timestamp from current time * @private * @return {number} Current time in seconds */ _getTimeStamp() { return Math.floor(Date.now() / 1000); // tslint:disable-line } } exports.OAuth = OAuth; exports.default = OAuth; // tslint:disable-line //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2F1dGguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvb2F1dGgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFDQSw2Q0FBNkM7QUFDN0MsaUNBQWlDO0FBRWpDLHFDQUE0QztBQUM1QyxtQ0FBNEI7QUFFNUI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1ERztBQUNIO0lBSUk7Ozs7Ozs7Ozs7Ozs7Ozs7T0FnQkc7SUFDSCxZQUFZLE9BQTJCLEVBQUU7UUFDckMsRUFBRSxDQUFBLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztZQUNoQixNQUFNLElBQUksS0FBSyxDQUFDLDZCQUE2QixDQUFDLENBQUM7UUFDbkQsQ0FBQztRQUVELElBQUksQ0FBQyxLQUFLLEdBQWMsTUFBTSxDQUFDLE1BQU0sQ0FBQztZQUNsQyxZQUFZLEVBQUUsRUFBRTtZQUNoQixnQkFBZ0IsRUFBRSxXQUFXO1lBQzdCLE9BQU8sRUFBRSxLQUFLO1lBQ2QsY0FBYyxFQUFFLElBQUk7WUFDcEIsbUJBQW1CLEVBQUUsSUFBSTtTQUM1QixFQUFFLElBQUksRUFBRSxFQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUSxFQUFDLENBQUMsQ0FBQztJQUN4QyxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsS0FBSyxDQUFDLEdBQVcsRUFBRSxHQUFXO1FBQzFCLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7WUFDaEIsbUJBQW1CO1lBQ25CLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFDaEUsQ0FBQztRQUNELE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUNsQyxDQUFDO0lBRUQ7Ozs7OztPQU1H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