Upoading Media no longer works

ios

#1

Uploading a photo from my app to the user’s Twitter account was working fine, and all of a sudden I get this error:

Error Domain=TwitterAPIErrorDomain Code=32 “Request failed: unauthorized (401)” UserInfo={NSErrorFailingURLKey=https://upload.twitter.com/1.1/media/upload.json, NSLocalizedDescription=Request failed: unauthorized (401), NSLocalizedFailureReason=Twitter API error : Could not authenticate you. (code 32)}

Here’s how I start the Twitter session:

Twitter.sharedInstance().logInWithCompletion({ (session, error) -> Void in
})

And then here’s how I upload the media to Twitter, with the error coming back from the sendtwitterRequest method:

let store = Twitter.sharedInstance().sessionStore

if let userid = store.session()?.userID {

  let client = TWTRAPIClient(userID: userid) //we have this from logInWithCompletion() in the previousVC

  let imageData = UIImagePNGRepresentation(self.cardView.takeSnapshot())
  let uploadParams = ["media" : imageData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)]
  //TODO: Handle error properly (do / catch?)
  let uploadRequest = client.URLRequestWithMethod(kTwitterPOSTmethod, URL: kTwitterUploadURL, parameters: uploadParams, error: nil)

  //First we upload the image via a request
  client.sendTwitterRequest(uploadRequest, completion: { (response, uploadResultData, error) -> Void in

…with the sendTwitterRequest method returning the error cited above.

Any ideas? I updated the TwitterKit SDK and it still gives me the “Could not authenticate you” error.


#2

I am experiencing the same issue. Everything worked fine up until a few weeks ago and the exact same code produces a 401 failure now.

My code looks almost identical to the above with the exception that I’m using UIImageJPEGRepresentation to convert a UI image into a base64 string to post.

Authentication is successful and I’m logging the user ID off the constructed client object that TWTRAPIClient is returning and seeing the proper ID. The error object from .sendTwitterRequest is nil as well.

Not sure what else to try here!


#3

@vikzilla For what it’s worth, it looks like you are using the wrong parameter to send media. Since you are encoding your image as a base64 string your parameters should look as follows:

let uploadParams = ["media_data" : imageData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)]

See https://dev.twitter.com/rest/reference/post/media/upload


#4

That returns the same error, unfortunately. I’ve even updated TwitterKit to 1.15.1


#5

Same results here unfortunately. Just figured I’d help potentially rule out one of the possibilities.

Here’s my code for reference. Again, very similar to yours and I’ve updated to 1.15.1 as well with no success:

Twitter.sharedInstance().logInWithCompletion { (user, error) in
  self.uploadMedia()
}

And here’s the start of the uploadMedia function. I’ve previously tried to pass the userID from the user object returned with logInWithCompletion but the code below now uses the userID from the shared session storage.

let client = TWTRAPIClient(userID: Twitter.sharedInstance().sessionStore.session()!.userID)
let endpoint = "https://upload.twitter.com/1.1/media/upload.json"
let mediaParams = ["media_data": SocialShare.sharedInstance.sharedImageData]
let uploadRequest = client.URLRequestWithMethod("POST", URL: endpoint, parameters: mediaParams, error: nil)

client.sendTwitterRequest(uploadRequest) { (response, data, error) -> Void in

Previously, no issues. Now, after I login to Twitter via the presented UIWebView I see login successful and the sheet is dismissed. Shortly thereafter I get the following error logged:

Error Domain=TwitterAPIErrorDomain
Code=32 "Request failed: unauthorized (401)"
UserInfo={
  NSLocalizedFailureReason=Twitter API error : Could not authenticate you. (code 32),
  NSErrorFailingURLKey=https://upload.twitter.com/1.1/media/upload.json,
  NSLocalizedDescription=Request failed: unauthorized (401)
}

I’ve logged the TWTRAPIClient and the user being passed in the request and everything looks correct. I’ve regenerated my application keys from the developer console.


#6

I’ve modified my code to skip the upload media step and found that it functions perfectly as long as an upload is not attempted.

So as of now it appears that /1.1/media/upload.json requests are broken with the 401 while /1.1/statuses/update.json continue to work as expected.

One last thing to note is that this behavior is experienced when logging into a Twitter account using the web view sheet oAuth flow. I have not attempted to replicate with a built-in System account as my application requires guest users to perform this functionality.

Can we get an official response here?


#7

Thank you for sharing all of that. I also found that I could successfully upload text statuses, but only when uploading any media along with the tweet do I get the error.

At least you and I both know we’re not the only ones experiencing this issue. Hopefully we can get this addressed soon.


#8

I’m a bit concerned by the lack of staff response to this thread. Is this in the appropriate category or is there some other way to get some developer support here?


#9

Yes, this is pretty concerning given the fact that multiple are experiencing the issue, which appears to be a bug on Twitter’s end. Disappointing…


#10

Authentication issues tend to be around the Twitter key being used or how it’s being initialized. Were your keys generated via Fabric or from apps.twitter.com?


#11

I believe the keys were originally generated via apps.twitter.com. But we did use Fabric to install TwitterKit into our Xcode project.


#12

Mine were also generated from apps.twitter.com. I’ve attempted to re-generate the keys and use them in case it was an authentication issue but got the same error messages.

Again, the strange thing here is it does not appear to be an all-around authentication issue as all other endpoints are just fine. Uploading media is the only endpoint that fails. I can post a tweet without uploading media just fine using my existing keys.


#13

What @albertcmartin says here is also true for me: I can successfully post text statuses, but when trying to upload media we get the error.


#14

@bonnell Any ideas here?


#15

@albertcmartin @vikzilla The media upload endpoint requires a multipart/form-data request. OAuth is handled differently for this type of request so when you make the request the OAuth signature is invalid which is why you are getting the unauthenticated errors. To upload media you will need to alter your request to be a multipart-form upload and you will need to set your content type correctly before you send the request.

We will be exposing support for uploading media in a future update but I don’t have a timeframe for that yet.


#16

Thank you for the response. I am using the TWTRAPIClient to construct and send these requests. Specifically, for this upload I am using the following method:

- (NSURLRequest *)URLRequestWithMethod:(NSString *)method URL:(NSString *)URLString parameters:(twtr_nullable NSDictionary *)parameters error:(NSError **)error;

Am I able to make the appropriate changes to accommodate multipart/form encoding and content type with your API client or do I have to do this manually? Can you point me to documentation on how this applies specifically when working with the TWTRAPIClient on iOS?

I am a bit confused since this method was working without the additional encoding up until the early part of this year.


#17

@chaselatta Is multipart/form-data a new requirement for uploading media? Because like @albertcmartin this was working fine a few months ago:

    let store = Twitter.sharedInstance().sessionStore
    
    if let userid = store.session()?.userID {
      
      let client = TWTRAPIClient(userID: userid) //we have this from calling logInWithCompletion() in the previous vc
      let imageData = UIImagePNGRepresentation(self.cardView.takeSnapshot())
      let uploadParams = ["media" : imageData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)]
      let uploadRequest = client.URLRequestWithMethod(kTwitterPOSTmethod, URL: kTwitterUploadURL, parameters: uploadParams, error: nil)
      
      //First we upload the image via a request
      client.sendTwitterRequest(uploadRequest, completion: { (response, uploadResultData, error) -> Void in

Still getting the error:

Error Domain=TwitterAPIErrorDomain Code=32 “Request failed: unauthorized (401)” UserInfo={NSErrorFailingURLKey=https://upload.twitter.com/1.1/media/upload.json, NSLocalizedDescription=Request failed: unauthorized (401), NSLocalizedFailureReason=Twitter API error : Could not authenticate you. (code 32)}


#19

@chaselatta Any update here?


#20

I’ve kept fiddling with it and same results :confused:


#21

@albertcmartin I am not sure why it was working for you before but multipart form has always been a requirement for the media upload endpoint. Which version of TwitterKit were you using before you upgraded? We made some changes to our networking stack in version 1.11 so something might have broken if you were using a version which was older than that but we haven’t touched that code since then.

We are going to make our media upload function a public method in our next release so you can use that method when it is released.