Отправлять подписанные запросы OAuth1.0.
/** * Adds a OAuth1 object to the global scope. This can be used as follows: * * const urlFetch = OAuth1.withAccessToken(consumerKey, consumerSecret, * accessToken, accessSecret); * const response = urlFetch.fetch(url, params, options); */ (function(scope) { /** * Creates an object to provide OAuth1-based requests to API resources. * @param {string} consumerKey * @param {string} consumerSecret * @param {string} accessToken * @param {string} accessSecret * @constructor */ function OAuth1UrlFetchApp( consumerKey, consumerSecret, accessToken, accessSecret) { this.consumerKey_ = consumerKey; this.consumerSecret_ = consumerSecret; this.accessToken_ = accessToken; this.accessSecret_ = accessSecret; } /** * Sends a signed OAuth 1.0 request. * @param {string} url The URL of the API resource. * @param {?Object.<string>=} opt_params Map of parameters for the URL. * @param {?Object.<string>=} opt_options Options for passing to UrlFetchApp * for example, to set the method to POST, or to include a form body. * @return {?Object} The resulting object on success, or null if a failure. */ OAuth1UrlFetchApp.prototype.fetch = function(url, opt_params, opt_options) { const oauthParams = { 'oauth_consumer_key': this.consumerKey_, 'oauth_timestamp': parseInt(new Date().getTime() / 1000), 'oauth_nonce': this.generateNonce_(), 'oauth_version': '1.0', 'oauth_token': this.accessToken_, 'oauth_signature_method': 'HMAC-SHA1' }; const method = 'GET'; if (opt_options && opt_options.method) { method = opt_options.method; } if (opt_options && opt_options.payload) { const formPayload = opt_options.payload; } const requestString = this.generateRequestString_(oauthParams, opt_params, formPayload); const signatureBaseString = this.generateSignatureBaseString_(method, url, requestString); const signature = Utilities.computeHmacSignature( Utilities.MacAlgorithm.HMAC_SHA_1, signatureBaseString, this.getSigningKey_()); const b64signature = Utilities.base64Encode(signature); oauthParams['oauth_signature'] = this.escape_(b64signature); const fetchOptions = opt_options || {}; fetchOptions['headers'] = { Authorization: this.generateAuthorizationHeader_(oauthParams) }; if (fetchOptions.payload) { fetchOptions.payload = this.escapeForm_(fetchOptions.payload); } return UrlFetchApp.fetch( this.joinUrlToParams_(url, opt_params), fetchOptions); }; /** * Concatenates request URL to parameters to form a single string. * @param {string} url The URL of the resource. * @param {?Object.<string>=} opt_params Optional key/value map of parameters. * @return {string} The full path built out with parameters. */ OAuth1UrlFetchApp.prototype.joinUrlToParams_ = function(url, opt_params) { if (!opt_params) { return url; } const paramKeys = Object.keys(opt_params); const paramList = []; for (const key in opt_params) { paramList.push(`${key}=${opt_params[key]}`); } return `${url}?${paramList.join('&')}`; }; /** * Generates a random nonce for use in the OAuth request. * @return {string} A random string. */ OAuth1UrlFetchApp.prototype.generateNonce_ = function() { return Utilities .base64Encode(Utilities.computeDigest( Utilities.DigestAlgorithm.SHA_1, parseInt(Math.floor(Math.random() * 10000)))) .replace(/[\/=_+]/g, ''); }; /** * Creates a properly-formatted string from a map of key/values from a form * post. * @param {!Object.<string>} payload Map of key/values. * @return {string} The formatted string for the body of the POST message. */ OAuth1UrlFetchApp.prototype.escapeForm_ = function(payload) { const escaped = []; for (const key in payload) { escaped.push(`${this.escape_(key)}=${this.escape_(payload[key])}`); } return escaped.join('&'); }; /** * Returns a percent-escaped string for use with OAuth. Note that * encodeURIComponent is not sufficient for this as the Twitter API expects * characters such as exclamation-mark to be encoded. See: * https://dev.twitter.com/discussions/12378 * @param {string} str The string to be escaped. * @return {string} The escaped string. */ OAuth1UrlFetchApp.prototype.escape_ = function(str) { return encodeURIComponent(str).replace(/[!*()']/g, function(v) { return '%' + v.charCodeAt().toString(16); }); }; /** * Generates the Authorization header using the OAuth parameters and * calculated signature. * @param {!Object} oauthParams A map of the required OAuth parameters. See: * https://dev.twitter.com/oauth/overview/authorizing-requests * @return {string} An Authorization header value for use in HTTP requests. */ OAuth1UrlFetchApp.prototype.generateAuthorizationHeader_ = function( oauthParams) { const params = []; for (const key in oauthParams) { params.push(`${key}="${oauthParams[key]}"`); } return `OAuth ${params.join(', ')}`; }; /** * Generates the signature string for the request. * @param {string} method The HTTP method e.g. GET, POST * @param {string} The URL. * @param {string} requestString The string representing the parameters to the * API call as constructed by generateRequestString. * @return {string} The signature base string. See: * https://dev.twitter.com/oauth/overview/creating-signatures */ OAuth1UrlFetchApp.prototype.generateSignatureBaseString_ = function( method, url, requestString) { return [method, this.escape_(url), this.escape_(requestString)].join('&'); }; /** * Generates the key for signing the OAuth request * @return {string} The signing key. */ OAuth1UrlFetchApp.prototype.getSigningKey_ = function() { return this.escape_(this.consumerSecret_) + '&' + this.escape_(this.accessSecret_); }; /** * Generates the request string for signing, as used to produce a signature * for the Authorization header. see: * https://dev.twitter.com/oauth/overview/creating-signatures * @param {!Object} oauthParams The required OAuth parameters for the request, * see: https://dev.twitter.com/oauth/overview/authorizing-requests * @param {?Object=} opt_params Optional parameters specified as part of the * request, in map form, for example to specify /path?a=b&c=d&e=f... etc * @param {?Object=} opt_formPayload Optional mapping of pairs used in a form * as part of a POST request. * @return {string} The request string */ OAuth1UrlFetchApp.prototype.generateRequestString_ = function( oauthParams, opt_params, opt_formPayload) { const requestParams = {}; const requestPath = []; for (let i = 0; i < arguments.length; i++) { const mapping = arguments[i]; if (mapping) { const paramKeys = Object.keys(mapping); for (let j = 0, paramKey; paramKey = paramKeys[j]; j++) { requestParams[paramKey] = mapping[paramKey]; } } } for (const key in requestParams) { requestPath.push(`${this.escape_(key)}=${this.escape_(requestParams[key])}`); } return requestPath.join('&'); }; /** * Builds a OAuth1UrlFetchApp object based on supplied access token (and other * parameters. * @param {string} consumerKey * @param {string} consumerSecret * @param {string} accessToken * @param {string} accessSecret * @return {!OAuth1UrlFetchApp} */ function withAccessToken( consumerKey, consumerSecret, accessToken, accessSecret) { return new OAuth1UrlFetchApp( consumerKey, consumerSecret, accessToken, accessSecret); } scope.OAuth1 = {withAccessToken: withAccessToken}; })(this);