ikerhurtado.com
You're in
Iker Hurtado's pro blog
Developer | Entrepreneur | Investor
Software engineer (entrepreneur and investor at times). These days doing performant frontend and graphics on the web platform at Barcelona Supercomputing Center

How to programmatically post on Twitter

1 Dec 2014   |   iker hurtado  
Share on Twitter Share on Google+ Share on Facebook
In this post I will explain how to create an HTTP request to publish a message (tweet) on Twitter. I also provide code excerpts (Go) of my implementation and I link relevant official documentation pages.

My current need is to post tweets occasionally in my account from the server. Luckily Twitter API let do it by a single request. Thus, for each request: authentication, authorization and write operations occur.

This is called by Andrew Arnott in this article 0-legged OAuth. I extract a snippet:

0-legged OAuth: skips all three legs by having consumers simply access their own protected resources at the service provider using OAuth signatures based on a private consumer key and secret. Analogous to username/password for the consumer app. Twitter access token option: also skips all three legs, but still provides access to a user's pre-existing data via a non-empty, manually obtained access token.

Twitter keep using the OAuth 1.0a protocol, so we have to encrypt the message before sending; this is what further complicates the implementation of the request. We go step by step.

Building the request

I start form these two links containing detailed information on how the encrypted request is constructed:

Authorizing a request | Twitter Developers

Creating a signature | Twitter Developers

The only difference between the example in the documentation and witch I present here is that I put the message in the URL (and therefore do not need to include include_entities=true in the URL).

The request has to carry in the header the parameter Authorization in order to be authorized. This is composed of 7 inner parameters, like this (the new line is included for readability):

OAuth oauth_consumer_key="xvz1evFS4wEEPTGEFPHBog", 
oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg", 
oauth_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D", 
oauth_signature_method="HMAC-SHA1", 
oauth_timestamp="1318622958", 
oauth_token="370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb", 
oauth_version="1.0"

Some values are fixed or easy to get (read the documentation), and others can be found in the control panel of of your application on Twitter: dev.twitter.com/apps

oauth_consumer_key is the application identifier (API key) and oauth_token is a typical access token of the OAuth protocol. This gives the application permission to access user data (me in this case) but, unlike other services, the token validity is undefined (if not explicitly renewed).

This could be a way to store all data in a structure (one map in Go):

const URL= "https://api.twitter.com/1.1/statuses/update.json"
const METHOD= "POST"
const CosumerKey= "ryJS8aRi98cGJkIeh63sd"
const SignMethod= "HMAC-SHA1"
const AccessToken= "162022596-0ssPdsfHJGlrjtF56HjpRE98V2gBU715Q0jHr"
const OauthVer= "1.0"
	
p := make(map[string]string) // Params map
	
p["status"] = msg  // Passed like param
		
p["oauth_consumer_key"] = CosumerKey
p["oauth_nonce"] = strconv.FormatInt(rand.Int63(), 10)
p["oauth_signature_method"] = SignMethod
p["oauth_timestamp"] =  strconv.FormatInt(time.Now().Unix(), 10)
p["oauth_token"]= AccessToken
p["oauth_version"] = OauthVer

Signature creation

The only one of the above parameters tricky to obtain is the signature (oauth_signature).

The first is to make up the parameter string. It is well explained in the official documentation; here I put my implementation in Go:

// Return the parameter string to compute signatures.

func getParameterString(params map[string]string) string {

   // Extract and sort the keys 
   keys := make([]string, len(params))
   i := 0
   for k, _ := range params {
      keys[i] = k
      i++
   }
   // Sort keys before percent encoding unlike documentation algorithm
   sort.Strings(keys)  
	
   var str string 
   // For each key it adds the pair key/value to the parameter string
   for _, k := range keys {
      str += PercentEncode(k)+"="+PercentEncode(params[k])+"&"
   }
   return str[:len(str)-1]
}

Now it's moment to create the string based signature:

// Calculates the signature base string
signBaseStr:= METHOD +"&"+ PercentEncode(URL) +"&"+ PercentEncode(paramString)

As the documentation says, you have to reapply percent encoding to the parameter string obtained in the previous step. As a result, there is a double URL encoding that is rather strange.

Finally, we must obtain the signing key.For that we need the consumer secret (secret API), which is the secret key of the application and the OAuth token secret, which is the token secret of the user account.

That would be the calculation of the firm:

// Singing
hash := hmac.New(sha1.New, []byte(signingKey))
hash.Write([]byte(signBaseStr))
signature := hash.Sum(nil)
digest := make([]byte, base64.StdEncoding.EncodedLen(len(signature)))
base64.StdEncoding.Encode(digest, signature)
var signatureString= string(digest)

Finishing the request

In order to create the request header, we finally need to add to the map the parameter that was missing: the signature (oauth_signature). On the other hand, we have to remove the parameter status from the map, since it is not necessary now(it was previously to create the signature). Simple:

p["oauth_signature"] = signatureString
	
delete(p, "status")

Lastly, we need the function that constructs the http request form the URL and the parameters that will form the authorization header:

func buildRequest(urlStr string,oauthHeaders map[string]string) *http.Request{
	
  req, _:= http.NewRequest("POST", urlStr, nil)
	
  headerValue:= "OAuth "
  for k, v := range oauthHeaders { 
    // Doesnt percent encode the key (not necessary)
    headerValue+= k + "=\"" + PercentEncode(v) + "\", " 
  }
  headerValue= headerValue[:len(headerValue)-2] // Remove the final ", "

  req.Header = map[string][]string{
    "Authorization": {headerValue},
  }

  return req
}

Now it only remains to send the made request.


POST A COMMENT: