Tuesday, February 26, 2013

Authenticating to OAuth Services Using AdWords Scripts

Many APIs today use OAuth in order for users to authenticate and call their service.  One very popular example is the Twitter REST API.  Digging through the AdWords Scripting reference, you'll notice that one of the cool things you can do with AdWords Scripts is to pull data from a URL using the UrlFetchApp.

So the question I asked was "Could I write a script to authenticate using OAuth and retrieve data from the Twitter REST API?"

Obviously, the first place I looked was in the OAuthConfig class of the UrlFetchApp.  I set up the configuration and tried to make a call but I kept getting an authentication error.  A little research showed that I wasn't the only one having this problem.  The issue stems from the fact that normally, users need to authenticate through some sort of dialog box before they can access data. See this example about connecting to Picasa Web Albums for more details.

But then I found a post about someone managing to get around this by re-implementing the OAuth authentication system in their script and using a standard UrlFetchApp.fetch() request.

I was up for a challenge so I recreated it myself.  It turned out to be quite a bit of code, but in the end, the only thing you really need to worry about is getting the correct keys from Twitter and calling _build_authorization_string().


// Authenticate and Connect to OAuth Service
// Created By: Russ Savage
// FreeAdWordsScripts.com
function main() {
  //Define the Twitter Keys and Secrets
  //More info on obtaining these can be found at https://dev.twitter.com/docs/auth/tokens-devtwittercom
  var oauth_key_stuff = {
    "consumer_key" : "your consumer key",
    "consumer_secret" : "your consumer secret",
    "access_token" : "your access token",
    "access_token_secret" : "your access token secret"

  // Update this with the REST url you want to call.  I only tested it with GET
  // but i don't think there is anything stopping a POST request from working.
  var url_stuff = {  
    "http_method" : 'GET',
    "base_url" : "https://api.twitter.com/1.1/statuses/user_timeline.json"

  //Add the parameters for the REST url you want to call.
  var url_param_stuff = {
    "screen_name" : "russellsavage" //hey that's me!
  // Don't touch this stuff
  var other_oauth_data = {
    "oauth_nonce" : Utilities.base64Encode(Math.random() +
          "secret_sauce" +
          (new Date()).getTime()).replace(/(?!\w)/g, ''),
    "oauth_signature_method" : "HMAC-SHA1",
    "oauth_timestamp" : Math.round((new Date()).getTime() / 1000.0),
    "oauth_version" : "1.0"
  // Here is where the magic happens
  var auth_string = _build_authorization_string(oauth_key_stuff,url_stuff,url_param_stuff,other_oauth_data);

  var options = {
    "headers" : { "Authorization" :  auth_string }
  var url = _build_url(url_stuff,url_param_stuff);
  var response = UrlFetchApp.fetch(url, options);
  var tweets = JSON.parse(response.getContentText());
  //now let's log my amazing tweets!
  for(var tweet in tweets) {
    var t = tweets[tweet];
  function _build_url(base_url,param_stuff){
    var url = base_url.base_url;
    if(param_stuff != {}) {
      url += '?';
    for(var key in param_stuff) {
      url += key + "=";
      url += encodeURIComponent(param_stuff[key]);
      url += '&';
    return url.slice(0,-1);
  function _build_param_string(auth_keys,url_data,oauth_data) {
    var data_for_param_string = {
      "oauth_consumer_key" : auth_keys.consumer_key,
      "oauth_nonce" : oauth_data.oauth_nonce,
      "oauth_signature_method" : oauth_data.oauth_signature_method,
      "oauth_timestamp" : oauth_data.oauth_timestamp,
      "oauth_token" : auth_keys.access_token,
      "oauth_version" : oauth_data.oauth_version
    // add additional url values
    for(var my_key in url_data) { 
      data_for_param_string[my_key] = url_data[my_key]; 
    // find and sort the keys for later
    var keys = [];
    for(var key in data_for_param_string) {
    //finally build and return the param string
    var param_string = "";
    for(var i in keys) {
      param_string += keys[i] + "=" + encodeURIComponent(data_for_param_string[keys[i]]);
      if(i < keys.length - 1) {
        param_string += "&";
    return param_string;
  function _build_sig_base_string(my_url_stuff,my_param_string) {
    return my_url_stuff.http_method +
      "&" + encodeURIComponent(my_url_stuff.base_url) +
      "&" + encodeURIComponent(my_param_string);
  function _build_sigining_key(my_key_stuff) {
    return encodeURIComponent(my_key_stuff.consumer_secret) + 
      "&" + encodeURIComponent(my_key_stuff.access_token_secret);
  function _build_oauth_signature(base_string,sign) {
    return Utilities.base64Encode(
  function _build_authorization_string(my_key_stuff,my_url_stuff,my_url_param_stuff,my_oauth_stuff) {
    var param_string = _build_param_string(my_key_stuff,my_url_param_stuff,my_oauth_stuff);
    var sig_base_string = _build_sig_base_string(my_url_stuff,param_string);
    var signing_key = _build_sigining_key(my_key_stuff);
    var oauth_signature = _build_oauth_signature(sig_base_string,signing_key);
    return "OAuth " +
           encodeURIComponent("oauth_consumer_key") + '="' + 
             encodeURIComponent(my_key_stuff.consumer_key) + '", ' +
           encodeURIComponent("oauth_nonce") + '="' + 
             encodeURIComponent(my_oauth_stuff.oauth_nonce) + '", ' +
           encodeURIComponent("oauth_signature") + '="' + 
             encodeURIComponent(oauth_signature) + '", ' +
           encodeURIComponent("oauth_signature_method") + '="' + 
             encodeURIComponent(my_oauth_stuff.oauth_signature_method) + '", ' +
           encodeURIComponent("oauth_timestamp") + '="' + 
             encodeURIComponent(my_oauth_stuff.oauth_timestamp) + '", ' +
           encodeURIComponent("oauth_token") + '="' + 
             encodeURIComponent(my_key_stuff.access_token) + '", ' +
           encodeURIComponent("oauth_version") + '="' + 
             encodeURIComponent(my_oauth_stuff.oauth_version) + '"';


  1. Thanks for sharing... This site altogether is extremely helpful since scripts listed here are simple enough for educational purposes yet can be used as is within real everyday business processes. I've noticed that everything here is a notch more advanced than in Google's tutorials. Great work!

  2. You need to visit this page to improve your writing skill! Have a good luck!

  3. The Collection Marts is platform where you can view latest designs about home décor and bedding. We have large range in different categories with finest fabric in cotton and silk. You can view not only present trends but also view huge collection with reasonable price. bed sheet set online , queen bed sheet sets , premium bed sheets , bridal bed sheets online shopping , all seasons duvet , vicky razai , sofa covers canada , velvet quilt , razai cover design The Collection Marts can provide fast service about delivery as well as customer support too. Our products are not only self-made but also, well connected with markets to ensure for possibility of available designs if client want to purchase. The Collection Marts customer support open 24/7 to guide their customers about material or product stuff.

  4. Your blogs are great.Are you also searching for Pico assignment? we are the best solution for you. We are best known for delivering nursing writing services to students without having to break the bank.

  5. I love your creativity. Are you also searching for academic nursing admission essay? we are the best solution for you. We are best known for delivering quality essay writing services to students without having to break the bank