401 Unauthorized error when trying to post a video from a Unity3d app

media-upload
unity

#1

Hello everyone,
I’m working on a unity based app that needs to be able to grab a video from an online server and allow users to login to twitter, share it and log out. I’ve spent almost 2 days now trying to get this to work but I’m stumped on this problem now. My app uses the twitterkit for unity and my workflow is as follows:

  • Initialize twitter using Twitter.Init
  • have user log in when a button is pressed using Twitter.Login()
  • in the login success callback call my video upload function passing it the TwitterSession object
  • I pull the video file from the server using a standard WWW class and convert it into bytes
  • I generate the Authorization header using the consumer keys and the authToken keys and it comes out like this:
    OAuth oauth_consumer_key=“", oauth_nonce=“4ArpK0iUiQ7aJERw1EcgDJdVZ64r0eNrSq0HYpGQ”, oauth_signature=“eK5DUWFvhFEplfkHouDMmSr9iP0%3D”, oauth_signature_method=“HMAC-SHA1”, oauth_timestamp=“1501799767”, oauth_token="**”, oauth_version=“1.0”
  • Next I start posting the video using the chunk post media method following the Twitter dev documentation by sending a request with this link and attaching the header:
    https://upload.twitter.com/1.1/media/upload.json?command=INIT&total_bytes=383631&media_type=video/mp4
    I also have code to do the append and the finalize requests but I get the 401 error on this first request I send out. I have tried a lot of different things and read pretty much all of the twitter documentations, Im thinking maybe its the way I am generating the signature key. on the docs it says that the signature key is generated from every other parameter in the header, another page on the docs mention that for media uploads I have to discard the post and url when generating it, I have tried both ways and Im still getting the same error. I’ll post some code here maybe someone can point me to what Im doing wrong.

This is the function that I use to post the video:

IEnumerator StartTwitterVideoUpload(TwitterSession _session)
	{
		yield return new WaitForEndOfFrame();

		WWW www;
		WWWForm wwwForm;
		byte[] videoBytes;
		string mediaID;
		string baseurl = "https://upload.twitter.com/1.1/media/upload.json?";

		byte[] dummy = new byte[1];
		dummy[0] = 0;
		parameters = new Dictionary<string, string>();
		Hashtable headers = new Hashtable();
		headers["Authorization"] = GetHeaderWithAccessToken("POST", baseurl, TwitterKit.Unity.Settings.TwitterSettings.ConsumerKey, TwitterKit.Unity.Settings.TwitterSettings.ConsumerSecret, _session, parameters);
		Debug.Log (headers ["Authorization"]);

		//init
		www = new WWW("ServerLink/test.mp4");
		while(!www.isDone) {
			yield return null;
			debugUI.text = "progress : " + www.progress;
		}
		debugUI.text = "size : " + www.bytesDownloaded;
		videoBytes = www.bytes;

		string url = baseurl + "command=INIT&total_bytes=" + www.bytesDownloaded + "&media_type=video/mp4";

		www = new WWW (url, dummy, headers);
		yield return www;
		Debug.Log ("INIT " + www.url);
		if (string.IsNullOrEmpty (www.error)) {
                         //other code here
                } else {
                        //getting error 401 here!
                }

This is how I’m generating the signature key:

private static string GenerateSignature(string httpMethod, string url, Dictionary<string, string> parameters)
	{
		var nonSecretParameters = (from p in parameters
			where !SecretParameters.Contains(p.Key)
			select p);

		string signatureBaseString = string.Format(CultureInfo.InvariantCulture,
			"{0}",//&{1}&{2}",
			//httpMethod,                                                             try without this might not
			//UrlEncode(NormalizeUrl(new Uri(url))),                  be needed for video upload
			UrlEncode(nonSecretParameters));

		string key = string.Format(CultureInfo.InvariantCulture,
			"{0}&{1}",
			UrlEncode(parameters["oauth_consumer_secret"]),
			parameters.ContainsKey("oauth_token_secret") ? UrlEncode(parameters["oauth_token_secret"]) : string.Empty);

		HMACSHA1 hmacsha1 = new HMACSHA1(Encoding.ASCII.GetBytes(key));
		byte[] signatureBytes = hmacsha1.ComputeHash(Encoding.ASCII.GetBytes(signatureBaseString));
		return Convert.ToBase64String(signatureBytes);
	}

And this is the encoding function:

private static string UrlEncode(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return string.Empty;
		}

		value = Uri.EscapeDataString(value);

		value = Regex.Replace(value, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper());

		value = value
			.Replace("(", "%28")
			.Replace(")", "%29")
			.Replace("$", "%24")
			.Replace("!", "%21")
			.Replace("*", "%2A")
			.Replace("'", "%27");

		value = value.Replace("%7E", "~");
		return value;
	}

Thank you everyone for your help


Correct way to upload a video with Unity3d
#2

Video upload is not supported from the Twitter Kit Native Composer. You can upload video using the Twitter Composer, but of course the user would have to have the Twitter app installed in that case.


#3

@AdamCummings Thank you for the response, I figured this out by just going through a php script after having the user log in thorugh the sdk. But now I’m having an issue where calling Twitter.Logout() after I’m done uploading doesnt actually log out the user. Once I try to log in again it just goes back to the authorize page of the previous user that logged in. I’m trying to make this app to behave like a kyosk application where people do something log in to share and leave. Once the next person comes up it should be able to just have a fresh login without having to log out of the previous user. Is there a specific way the log out functionality works?


#5

Twitter.Logout only removes the session stored locally in your app. It doesn’t log you out of the Twitter app. For kiosk style applications you can post the Tweet to an account you run and tag the user. This way you avoid having to asked the user for their username and password.


#6

I see, that’s not really what I’m looking for, the whole idea is to have people post a video on their own feed. Would it work if I somehow clear the cache of my app or my internal browser? Cause the device my app is on does not have the twitter app, the twitter kit just triggers the internal browser to have users sign in.


#7

Clearing to browser is cookies is one option. I think asking your users for their user/password in a kiosk is going to be a major source of friction. We have a kiosk at Twitter that uses my proposed approach. A Twitter user can then retweet the original Tweet.


#8

Thank you for your help, I understand what your suggesting but I would want to try to get the log out functionality to work, is there no way for me to force to re-authenticate when logging in? or maybe manually expire the token? clearing the cache or closing the app also doesn’t seem to work.