update_with_media fails with 401 Unauthorized


#1

Using API1.1, I’m not able to post an update with an image. I think it is due to improperly signing the request… but I’m not sure. Using v1.1, POST, multipart data. Anyone know why this is happening? Please help!

Using the Twitter Developer Console results in a successful tweet with image.

Authorization request is successful however the subsequent update_with_media post returns the following error (using Fiddler)

Response

HTTP/1.1 401 Unauthorized
code=32
message=Could not authenticate you

The raw request is:
Request

POST https://api.twitter.com/1.1/statuses/update_with_media.json HTTP/1.1
Referer: app:/twitterTest.swf
Accept: text/xml, application/xml, application/xhtml+xml, text/html;q=0.9, text/plain;q=0.8, text/css, image/png, image/jpeg, image/gif;q=0.8, application/x-shockwave-flash, video/mp4;q=0.9, flv-application/octet-stream;q=0.8, video/x-flv;q=0.7, audio/mp4, application/futuresplash, */*;q=0.5
x-flash-version: 11,7,700,224
Content-Type: multipart/form-data, boundary=----------gL6ae0cH2Ef1Ij5GI3gL6GI3GI3ei4
X-Target-URI: https://api.twitter.com
Mime-Version: 1.0
X-HostCommonName: api.twitter.com
Authorization: OAuth oauth_consumer_key="{removed}", oauth_nonce="51915", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1390591324", oauth_token="{removed}", oauth_version="1.0", oauth_signature="{removed}"
Content-Length: 21692
Accept-Encoding: gzip,deflate
User-Agent: Mozilla/5.0 (Windows; U; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) AdobeAIR/3.7
Host: api.twitter.com
Connection: Keep-Alive
Cookie: guest_id=v1%3A138385821369315395; twll=l%3D1388514403; remember_checked=1; remember_checked_on=1; auth_token={removed}; secure_session=true; _twitter_sess=BAh7CToJdXNlcmkEXqxRHzoMY3NyZl9pZCIlNzcxNTliYzI4ODcxZjgzNjdi%250AYTJkYzJiMjU5MDY3NTg6B2lkIiU1MGVhZDhmNzYwZTcyODNiYTcwODIxODEx%250AMjdjMTllZDoPY3JlYXRlZF9hdGwrCFRWs8VDAQ%253D%253D--5ab20358d362c078fe03735acb98502695935ffe; twid=u%3D525446238%7CcJyhNJqYbXpS4mVk64IVAiEBVAk%3D; __utma=191792890.63151085.1383861162.1383861162.1383861162.1; __utmz=191792890.1383861162.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); external_referer=fJs6%2Fry34fDpYITA3edMEbH0DKq7KyB4%7C0; lang=en

------------gL6ae0cH2Ef1Ij5GI3gL6GI3GI3ei4
Content-Disposition: form-data; name="status"

This is a test @tester #testStatus>
------------gL6ae0cH2Ef1Ij5GI3gL6GI3GI3ei4
Content-Disposition: form-data; name="media[]"; filename="006c.JPG"
Content-Type: image/jpeg

����� JFIF�  �� � ������� 
{truncated}

The code being used to build the request is AS3 using AIR:

public function updateStatusWithMedia(status:String, imageData:BitmapData):void
		 {
			 var boundary:String = "----------gL6ae0cH2Ef1Ij5GI3gL6GI3GI3ei4";
			 var vars:URLVariables = new URLVariables();
			 vars.status = strEscape(status.substr(0,140));
			 
			 checkCredentials();
			 
			 request = URL_SEND_UPDATE_WITH_MEDIA;
			 _returnType = RETURN_TYPE_STATUS;
			 
			 var imageByteArray:ByteArray = new ByteArray();
			 var aJPGEncoder:JPGEncoder = new JPGEncoder();
			 imageByteArray = aJPGEncoder.encode(imageData);
			 
			 var mfd:MultipartFormData=new MultipartFormData(boundary);
			 mfd.addData("status",status);
			 mfd.addData("media[]",imageByteArray, "006c.JPG", "image/jpeg");
			 
			 var urlRequest2 : URLRequest = new URLRequest();
			 var signedData:String  = _oAuth.getSignedRequestForHeader( URLRequestMethod.POST, "https://api.twitter.com/1.1/statuses/update_with_media.json", null );
			 
			 var authHeader : URLRequestHeader = new URLRequestHeader("Authorization", "OAuth " + signedData);
			 var headers : Array = new Array();
			 headers.push(authHeader);
			 headers.push(getHostCommonNameHeader());
			 headers.push(getMimeHeader());
			 headers.push(new URLRequestHeader("X-Target-URI", "https://api.twitter.com"));
			 
			 urlRequest2.requestHeaders = headers;
			 urlRequest2.url =  serviceHost + request;
			 urlRequest2.contentType = "multipart/form-data, boundary=" + mfd.boundary;
			 urlRequest2.data = mfd.getByteArray();
			 urlRequest2.method = URLRequestMethod.POST;
			 
			 urlLoader.load(urlRequest2);   
		 }

#2

We have a documentation page on this topic at https://dev.twitter.com/docs/uploading-media.


#3

I’ve reviewed the documentation extensively - and implemented my code that way. I must be missing something - can anyone tell if I’m signing the request properly?


#4

Here is the code that is creating the signature:

public function getSignedRequestForHeader(method:String, url:String, urlVars:URLVariables = null):String { var args:Array = [];
		args.push({name: "oauth_consumer_key", value: consumerKey});
		args.push({name: "oauth_signature_method", value: "HMAC-SHA1"});
		args.push({name: "oauth_timestamp", value: time});
		args.push({name: "oauth_nonce", value: nonce});
		args.push({name: "oauth_version", value: "1.0"});
		args.push({name: "oauth_token", value: oauthToken});
		
		for (var nameValue:String in urlVars)
			args.push({name: nameValue, value: urlVars[nameValue]});
		
		args.sortOn("name");
		
		var n:int = args.length;
		var vars:String = "";
		for (var i:int = 0; i < n; i++)
		{
			if (args[i]["name"] != "_method")
			{
				vars += args[i]["name"]+"=\""+args[i]["value"];
				if (i != n)
					vars += "\", ";
			}
		}
		var signString:String = method.toUpperCase() +"&" + encodeURIComponent(url) + "&" + encodeURIComponent(vars);

		var hmac:HMAC =  Crypto.getHMAC("sha1");
		var key:ByteArray = Hex.toArray( Hex.fromString(encodeURIComponent(consumerSecret) + "&" + encodeURIComponent(oauthTokenSecret)));
		var data:ByteArray = Hex.toArray( Hex.fromString( signString ) );
		var sha:String = Base64.encodeByteArray( hmac.compute( key, data ) );
		vars += "oauth_signature=" + "\"" + encodeURIComponent(sha) + "\"";
		return vars;
	}

#5

Is there any other information I can provide in order to help troubleshoot? Does it look as if the signing is wrong? The signString being used in the above code is:

POST&https%3A%2F%2Fapi.twitter.com%2F1.1%2Fstatuses%2Fupdate_with_media.json&oauth_consumer_key%3D%2...g%22%2C%20oauth_nonce%3D%2257663%22%2C%20oauth_signature_method%3D%22HMAC-SHA1%22%2C%20oauth_timestamp%3D%221391028439%22%2C%20oauth_token%3D%22....zw%22%2C%20oauth_version%3D%221.0%22%2C%20

#6

@froginthevalley The url you provided above (https://dev.twitter.com/docs/uploading-media) stops short of detailing how the signature is obtained. I think this is the issue I’m facing.

Is there any information you can point me to or review how I’m creating the signature? Are the in the correct order? Did I include all of the oauth_* parameters in the headers? Am I encoding the strings correctly?

Support for Flex / Air had begun to wind down since the move to API1.1 and there are no Twitter APIs migrated to 1.1 that support update_with_media using POST.

Once I resolve this, I plan on adding to one of the only Flex Twitter libraries supporting 1.1:

Thanks for any help!


#7

RESOLVED
The issue was with not have the authorization header constructed correctly.

I used this handy site to deconstruct the signature:
http://oauth.googlecode.com/svn/code/javascript/example/signature.html

  • The authorization headers needed to be comma separated with the values enclosed in quotes.
  • No spaces in the signing string
  • Signing string parameters need to be separated by & (not by commas)

I’ll post updated code when ready.