Tumblr Photo Post Returns 401 (Not Authorized)

1,300 views
Skip to first unread message

Jabari Hunt

unread,
Jan 3, 2014, 11:27:59 AM1/3/14
to tumbl...@googlegroups.com
I'm attempting to use the Tumblr API in an Android app to authorize users and make text and photo posts.  I'm using the Scribe library.  So for, I can successfully obtain an access token and use it to get user info.  I can also make text posts without any issues.  This tells me that I'm signing requests correctly.

However, I've spent the last week attempting to make photo posts without success.  I continuously receive 401 errors (Not Authorized)  I've read through many posts on this support forum as well as on Stack Overflow, but was unable to find a solution.  I'm reluctant to include the Jumblr library because I'm trying to keep my app as lean as possible.  That said, I reviewed the Jumblr code and decided to mimic how photo posts are sent (https://github.com/tumblr/jumblr/blob/master/src/main/java/com/tumblr/jumblr/request/MultipartConverter.java).  I'm still receiving the exact same error.

Below is an example my multipart POST request and the response I receive.  I've replace the blog name, and OAuth signature, consumer key, and token variables, and have removed the binary image data for brevity sake.  Everything else is untouched.  I have a couple of questions...

  1. Are there any other variables that should be included in the multipart section? A Stack Overflow user stated that placing the "oauth_" signature variables in there fixed his problem.  I didn't have success with this, but maybe there was something I was missing.
  2. The Jumblr app doesn't appear to do any encoding of the image data, although the Tumblr documentation states that it should be URL encoded.  Right now I'm sending it as the Jumblr app appears to (raw binary).  Is this correct?
  3. Does anything else in my request look incorrect?


**********************************************
REQUEST
**********************************************

Content-Type: multipart/form-data, boundary=9bca53d1bf899bca53d1fd97
Authorization: OAuth oauth_signature="***REMOVED***", oauth_version="1.0", oauth_nonce="1031084836", oauth_signature_method="HMAC-SHA1", oauth_consumer_key="***REMOVED***", oauth_timestamp="1388726724", oauth_token="***REMOVED***"
Content-Length: 1194
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.3; SM-N900T Build/JSS15J)
Connection: Keep-Alive
Accept-Encoding: gzip

--9bca53d1bf899bca53d1fd97
Content-Disposition: form-data; name="type"
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit

photo
--9bca53d1bf899bca53d1fd97
Content-Disposition: form-data; name="caption"
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit

Another pic test...
--9bca53d1bf899bca53d1fd97
Content-Disposition: form-data; name="data[0]"; filename="postr_media_file_1388726724-1709648435.jpg"
Content-Type: image/jpeg
Content-Transfer-Encoding: binary

***** BINARY DATA REMOVED FOR BREVITY *****


**********************************************
RESPONSE
**********************************************

HTTP/1.1 401 Not Authorized
Server: nginx
Date: Fri, 03 Jan 2014 05:25:25 GMT
Content-Type: application/json; charset=utf-8
Connection: close
Set-Cookie: tmgioct=52c649c5c510450950980100; expires=Mon, 01-Jan-2024 05:25:25 GMT; path=/; httponly
P3P: CP="ALL ADM DEV PSAi COM OUR OTRo STP IND ONL"
Content-Length: 60

{"meta":{"status":401,"msg":"Not Authorized"},"response":[]}

Stephen Collins

unread,
Jan 3, 2014, 1:18:31 PM1/3/14
to tumbl...@googlegroups.com
This is just a guess, but this:

Content-Disposition: form-data; name="data[0]"; filename="postr_media_file_1388726724-1709648435.jpg"

Doesn't seem correct.  It may be choking on the "filename."  Keep in mind that 401 doesn't necessarily mean "Not Authorized" in the API; in my experience ANY problem with the request returns 401s.
Also, "data[0]" may not be the correct name for that parameter.  The docs do say it's an array, but I don't think that means the name should be denoted as such.

Just a guess.

Jabari Hunt

unread,
Jan 3, 2014, 3:21:38 PM1/3/14
to tumbl...@googlegroups.com
I think I accidentally replied directly to you Stephen...

For everyone else, I tried the suggestions (and various combinations of) but they were unsuccessful.  I've been banging my head for over a week now on this.  I appreciate any and all suggestions!

John Bunting

unread,
Jan 3, 2014, 3:50:39 PM1/3/14
to tumbl...@googlegroups.com
Multipart data parameters should not be included into the OAuth signature when signing. This may be catching you right now.


--
You received this message because you are subscribed to the Google Groups "Tumblr API Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to tumblr-api+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--
John Bunting

Simplicity is prerequisite for reliability
    --Edsger W. Dijkstra

Jabari Hunt

unread,
Jan 3, 2014, 4:07:10 PM1/3/14
to tumbl...@googlegroups.com
Hmmm...so I should sign the request before building the multipart form and adding it?

John Bunting

unread,
Jan 3, 2014, 4:14:50 PM1/3/14
to tumbl...@googlegroups.com
Yep. I believe jumblr is actually doing this when building the signature. 

It seems we're doing something in jumblr by passing in a file object, skip it in the construction of the signature, then add the multipart piece of the request to the end. 

You'll see some of this happening here: https://github.com/tumblr/jumblr/blob/master/src/main/java/com/tumblr/jumblr/request/RequestBuilder.java#L82 Just run through most of the rest of this code and you should see what you need :)

Hope that helps!

Jabari Hunt

unread,
Jan 3, 2014, 6:12:59 PM1/3/14
to tumbl...@googlegroups.com
OK, I made the following changes.  I'm still receiving a "Not Authorized" error, but I'm pretty sure the following corrections should be there regardless since it's what the Jumblr code generates:

  1. Generated a separate request and extracted the headers, them manually added them to the original request (in lieu of the Scribe signing method)
  2. Removed the "Content-Type" and "Content-Transfer-Encoding" attributes from the type and caption parts
  3. Removed the "Content-Transfer-Encoding" attribute from the data part
Here is an updated request...


**********************************************
REQUEST
**********************************************

Content-Type: multipart/form-data, boundary=c60f7c041c02c60f7c046e9b
Authorization: OAuth oauth_signature="***REMOVED***", oauth_version="1.0", oauth_nonce="315351812", oauth_signature_method="HMAC-SHA1", oauth_consumer_key="***REMOVED***", oauth_timestamp="1388785116", oauth_token="***REMOVED***"
Content-Length: 1001
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.3; SM-N900T Build/JSS15J)
Connection: Keep-Alive
Accept-Encoding: gzip

--c60f7c041c02c60f7c046e9b
Content-Disposition: form-data; name="type"

photo
--c60f7c041c02c60f7c046e9b
Content-Disposition: form-data; name="caption"

Another pic test...
--c60f7c041c02c60f7c046e9b
Content-Disposition: form-data; name="data[0]"; filename="postr_media_file_1388785116-1709648435.jpg"
Content-Type: image/jpeg

***** BINARY DATA REMOVED FOR BREVITY *****

Jabari Hunt

unread,
Jan 3, 2014, 6:18:09 PM1/3/14
to tumbl...@googlegroups.com
Also...I tried "data" and "data[0]".  I left the "file" attribute in there.

Jabari Hunt

unread,
Jan 7, 2014, 2:17:15 AM1/7/14
to tumbl...@googlegroups.com
I FINALLY have this working....

The key to doing it correctly is NOT just signing without the multipart form!!!  Here are the steps...
  1. Add all fields EXCEPT the data field as regular url encoded POST body variables
  2. Sign the request
  3. Remove ALL off the post variables you just added from the request
  4. Add the multipart form, including the data field this time
Some things to consider...
  • The Content-Type in the header should be "multipart/form-data"
  • The Content-Disposition of all form parts should be "form-data" and, of course, include a valid "name" attribute (ie. type, caption, etc...)
  • The Content-Disposition of the data part should also include a "filename" attribute
  • The only form part that should contain a Content-Type is data, and it should be set to the mime type of the file you are uploading (ie. "image/jpeg")
  • I used "data[0]" as the name of the data field.  I haven't tested this with just "data", but according to everything I've read it should work that way as well.  If you are creating a photo set, I believe you simple add additional parts (ie. data[1]. data[2], etc...).  Again, I haven't tested anything except "data[0]", so do your due diligence!!!
  • I did NOT encode the binary image data!!! I saw people spending considerable amount of time on this in other posts when adding the image as a POST body variable. If doing this as a multipart form, you can skip the encoding and send raw binary data!  ;-)
I hope this helps someone!  I've spent two weeks banging my head on random solid objects trying to figure this out.  The implementation is very easy to do, but there is zero documentation available on how exactly to build POST requests for photos properly.  The official docs really should include that.  If I had know what I just posted above I could have completed this in minutes instead of weeks!!!

The last request I posted earlier is still valid, but here it is again.  Just remember what I mentioned about the signature!!!

**********************************************
REQUEST
**********************************************

Content-Type: multipart/form-data, boundary=c60f7c041c02c60f7c046e9b
Authorization: OAuth oauth_signature="***REMOVED***", oauth_version="1.0", oauth_nonce="315351812", oauth_signature_method="HMAC-SHA1", oauth_consumer_key="***REMOVED***", oauth_timestamp="1388785116", oauth_token="***REMOVED***"
Content-Length: 1001
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.3; SM-N900T Build/JSS15J)
Connection: Keep-Alive
Accept-Encoding: gzip

--c60f7c041c02c60f7c046e9b
Content-Disposition: form-data; name="type"

photo
--c60f7c041c02c60f7c046e9b
Content-Disposition: form-data; name="caption"

Another pic test...
--c60f7c041c02c60f7c046e9b
Content-Disposition: form-data; name="data[0]"; filename="postr_media_file_1388785116-1709648435.jpg"
Content-Type: image/jpeg

***** BINARY DATA REMOVED FOR BREVITY *****
--c60f7c041c02c60f7c046e9b--
Reply all
Reply to author
Forward
0 new messages