Error Code 32: Could not authenticate you


#1

I’m having an issue with very basic functionality of the Twitter api - posting a status. Was hoping to start here and expand to more complex things such as uploading media. However, I’m stuck getting error code 32. I’ve looked around for the past few hours and nothing has helped. The main ‘solution’ I find is that the url encoding is usually off. To rule that out I’m using a short status message with no special characters, but am still receiving the error. Here’s an image of the request I’m sending out:

I’m hoping it’s something simple, I’ve been losing my mind over this. I’ve double and triple checked that all of the oauth info (keys etc.) is correct. The headers all look to be in order. I’m really not sure what the issue could be here.


#2

Generally speaking it is better to use a Twitter API library to do this kind of thing, because they handle all of the encoding and OAuth stuff for you. Another thing that is important to check is that your computer’s clock is accurate, as there’s a time dependency baked into OAuth.

If you want to craft this yourself, you can experiment with either the OAuth tool (linked just above the example output on the POST statuses/update page), the API console, or our command-line twurl utility, and compare the headers with what you’re sending.


#3

I’d rather not use an external library, as I have custom needs I’d like to include in my own library and would like to know exactly how my code works. Thank you for the suggestion though, I have been using the source of some libraries as reference.

That being said - my system time is accurate, so I don’t feel like that would be the issue. I have compared the sent headers between my requests and the console tool/OAuth tool on the doc pages, and they look exactly the same. (except for nonce, timestamp, signature of course) Which leads me to believe the only possible thing that could be wrong is my signature generation. Would you mind taking a look at my generation method and see if anything pops out as wrong to you? Sorry for the giant block of code, the generation process is fairly long. This is in C#.

    /* Helper classes */
    public class OAuthInfo
    {
        public string ConsumerKey { get; set; }
        public string ConsumerSecret { get; set; }
        public string AccessToken { get; set; }
        public string AccessSecret { get; set; }
    }

    /* OAuth Methods */
    public static string GenerateOAuthHeader(OAuthInfo Info, string Url)
    {
        string nonce = new Random().Next(0x0000000, 0x7fffffff).ToString("X8");
        string method = "HMAC-SHA1";
        string timestamp = ((int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString();
        string version = "1.0";

        var baseFormat2 = "oauth_consumer_key={0}&oauth_signature_method={1}&oauth_nonce={2}" +
                "&oauth_timestamp={3}&oauth_token={4}&oauth_version={5}";
        var baseString2 = string.Format(baseFormat2,
            Info.ConsumerKey,
            method,
            nonce,
            timestamp,
            Info.AccessToken,
            version);

        baseString2 = string.Concat("POST&", UtilityBelt.EncodeRFC3986(Url),
                     "&", UtilityBelt.EncodeRFC3986(baseString2));

        var compositeKey2 = string.Concat(UtilityBelt.EncodeRFC3986(Info.ConsumerSecret),
                                "&", UtilityBelt.EncodeRFC3986(Info.AccessSecret));

        string oauth_signature2;
        using (HMACSHA1 hasher = new HMACSHA1(ASCIIEncoding.ASCII.GetBytes(compositeKey2)))
        {
            oauth_signature2 = Convert.ToBase64String(
                hasher.ComputeHash(ASCIIEncoding.ASCII.GetBytes(baseString2)));
        }

        var headerFormat2 = "OAuth oauth_consumer_key=\"{0}\", oauth_nonce=\"{1}\", " +
                           "oauth_signature=\"{2}\", oauth_signature_method=\"{3}\", " +
                           "oauth_timestamp=\"{4}\", oauth_token=\"{5}\", " +
                           "oauth_version=\"{6}\"";

        var authHeader2 = string.Format(headerFormat2,
                UtilityBelt.EncodeRFC3986(Info.ConsumerKey),
                UtilityBelt.EncodeRFC3986(nonce),
                UtilityBelt.EncodeRFC3986(oauth_signature2),
                UtilityBelt.EncodeRFC3986(method),
                UtilityBelt.EncodeRFC3986(timestamp),
                UtilityBelt.EncodeRFC3986(Info.AccessToken),
                UtilityBelt.EncodeRFC3986(version)
                );

        return authHeader2;
    }

And the EncodeRFC3986 method:

    public static string EncodeRFC3986(string value)
    {
        if (string.IsNullOrEmpty(value))
            return string.Empty;

        var encoded = Uri.EscapeDataString(value);

        return System.Text.RegularExpressions.Regex
            .Replace(encoded, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper())
            .Replace("(", "%28")
            .Replace(")", "%29")
            .Replace("$", "%24")
            .Replace("!", "%21")
            .Replace("*", "%2A")
            .Replace("'", "%27")
            .Replace("%7E", "~");
    }

Perhaps the issue is encoding somewhere or something, but when comparing my code to other libraries it looks like I encode properly in all the right places, so I’m really not sure where an error could be.


#4

It looks like you are not including the status key and value in baseFormat2. I would recommend stepping through creating a signature using the exact example values until your signature matches the example signature.


#5

ah yes, I completely missed that. Thank you so much! That fixed the issue I was having. However, this brings me to another issue. If you could help me that would be amazing, otherwise I’ll create another thread.

Anyways, when uploading an image via media/upload, how would I properly generate a signature? I’m assuming it’s not quite the same as adding the data to the baseFormat2 string, but I’m not finding anything when searching around. I know the parameter that needs to be sent through is media_data, and the data should be the base64 string of the image, just not sure how to input that into my signature generation method.

Oh, btw, here’s what I changed in my generation method to make it work with queries that contain basic parameters:

    /* OAuth Methods */
    public static string GenerateOAuthHeader(OAuthInfo Info, string Url, List<KeyValuePair<string, string>> postParams)
    {
        string nonce = new Random().Next(0x0000000, 0x7fffffff).ToString("X8") + "laf8a34l3r8j";
        string method = "HMAC-SHA1";
        string timestamp = ((int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString();
        string version = "1.0";

        var baseFormat2 = "oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method={2}" +
                "&oauth_timestamp={3}&oauth_token={4}&oauth_version={5}&{6}";
        var postString1 = postParams.OrderBy(x => x.Key)
            .Select(x => string.Format("{0}={1}", x.Key, x.Value));
        var postString = string.Join("&", postString1);
        var baseString2 = string.Format(baseFormat2,
            Info.ConsumerKey,
            nonce,
            method,
            timestamp,
            Info.AccessToken,
            version,
            postString);

        baseString2 = string.Concat("POST&", UtilityBelt.EncodeRFC3986(Url),
                     "&", UtilityBelt.EncodeRFC3986(baseString2));

        var compositeKey2 = string.Concat(UtilityBelt.EncodeRFC3986(Info.ConsumerSecret),
                                "&", UtilityBelt.EncodeRFC3986(Info.AccessSecret));

        string oauth_signature2;
        using (HMACSHA1 hasher = new HMACSHA1(ASCIIEncoding.ASCII.GetBytes(compositeKey2)))
        {
            oauth_signature2 = Convert.ToBase64String(
                hasher.ComputeHash(ASCIIEncoding.ASCII.GetBytes(baseString2)));
        }

        var headerFormat2 = "OAuth oauth_consumer_key=\"{0}\", oauth_nonce=\"{1}\", " +
                           "oauth_signature=\"{2}\", oauth_signature_method=\"{3}\", " +
                           "oauth_timestamp=\"{4}\", oauth_token=\"{5}\", " +
                           "oauth_version=\"{6}\"";

        var authHeader2 = string.Format(headerFormat2,
                UtilityBelt.EncodeRFC3986(Info.ConsumerKey),
                UtilityBelt.EncodeRFC3986(nonce),
                UtilityBelt.EncodeRFC3986(oauth_signature2),
                UtilityBelt.EncodeRFC3986(method),
                UtilityBelt.EncodeRFC3986(timestamp),
                UtilityBelt.EncodeRFC3986(Info.AccessToken),
                UtilityBelt.EncodeRFC3986(version)
                );

        return authHeader2;
    }

#6

Because the method uses multipart POST, OAuth is handled a little differently. POST or query string parameters are not used when calculating an OAuth signature basestring or signature. Only the oauth_* parameters are used.

https://dev.twitter.com/rest/media/uploading-media


#7

Thank you for your help again. I should have posted here, but right after I made my last post I started researching and figured that out. I keep getting a media type unrecognized error now though for some reason. I believe the request is built correctly, here’s a snapshot:

POST https://upload.twitter.com/1.1/media/upload.json HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
Authorization: OAuth oauth_consumer_key="xxxxx", oauth_nonce="6F32FEBFlaf8a34l3r8j", oauth_signature="qz3G31xndU%2BBsGCrfWI6zT%2FwgC4%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1470191905", oauth_token="xxxxx", oauth_version="1.0"
Content-Type: multipart/form-data; boundary="ac2fac1f-8023-4f03-8f38-5cc831f6f02c"
Host: upload.twitter.com
Content-Length: 27565
Expect: 100-continue
Connection: Keep-Alive

--ac2fac1f-8023-4f03-8f38-5cc831f6f02c
Content-Type: image/png
Content-Disposition: form-data; name="media_data"; filename="Mockup.png"

(BASE 64 DATA)

--ac2fac1f-8023-4f03-8f38-5cc831f6f02c--

Is any of this glaring to you? Maybe I’m supposed to be using a specific image name or something, I’m not really sure.


#8

I’m a huge dope - I thought I was reading the file into a base 64 string but I was reading it as binary and should have been using the media parameter rather than media_data. Anyways, thank you so much for your help, everything is in working order now! Looking forward to working further with this api.


#9

Unfortunately it seems like the initial problem wasn’t quite solved after all.

Everything works splendidly when using basic characters that don’t require encoding. (a, b, c, etc.) When adding any character that is encoded (e.g. space to %20), I get the ‘could not authenticate you’ error all over again. I’m able to send ‘helloworld’ with no problems at all, but as soon as I try ‘hello world’ (which is encoded to hello%20world in the base string) I get the error. This is another thing I can’t seem to find when searching, because it seems like a problem with my understanding of OAuth rather than the Twitter api itself. Anyways, been trying to figure this out for a while tonight, but this seems like a simple problem with a simple solution that my tired eyes aren’t noticing.


#10

I would double check that the values of the parameters for baseString2 are getting RFC 3986 encoded before being concatenated with = and & and then again RFC 3986 encoded before being concatenated with POST and the URL.

You can see in the “Creating the signature base string” section of creating a signature that it has status%3DHello%2520Ladies towards the end. That started as Hello Ladies and was encoded once to status=Hello%20Ladies and then again to status%3DHello%2520Ladies.


#11

I thought about that as well and have tried, resulting in a baseString similar to the following:

POST&https%3A%2F%2Fapi.twitter.com%2F1.1%2Fstatuses%2Fupdate.json&oauth_consumer_key%3D[key]%26oauth_nonce%3D2CA902A9laf8a34l3r8j%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1470260644%26oauth_token%3D[token]%26oauth_version%3D1.0%26status%3Dhello%2520world

As you can see, hello world was encoded and then status=hello%20world was encoded for the baseString. It all looks to match up with the docs, but there’s obviously something small I’m missing with the encoding.


#12

Been racking my brain trying to figure out what I’m doing wrong, it feels like it’s something super simple that I’m just missing. I’ll update here if I figure it out with a working code sample.


#13

My recommendation is still to go through each step creating a signature with the exact keys/secrets/timestamps/etc in the example until the example signature matches your computed signature.


#16

Thanks so much for your help! I finally did figure it all out. I think at some point I was encoding one part of a string but not the other and then concat’ing them. Anyways, here is the complete method + helper class I came up with. I have successfully tweeted and tweeted with media using this. The problem was that I didn’t quite understand the signature creation process, I was just using copied code and expecting it to work. After going through the actual process and understanding exactly what is happening and where, I was able to write the new method from the ground up. Thanks so much again abraham!

    /* Helper classes */
    public class OAuthInfo
    {
        public string ConsumerKey { get; set; }
        public string ConsumerSecret { get; set; }
        public string AccessToken { get; set; }
        public string AccessSecret { get; set; }

        public static void AddOAuthParameters(List<KeyValuePair<string, string>> parameters, OAuthInfo Info, string version, string nonce, string method, string timestamp)
        {
            parameters.Add(new KeyValuePair<string, string>("oauth_version", version));
            parameters.Add(new KeyValuePair<string, string>("oauth_consumer_key", Info.ConsumerKey));
            parameters.Add(new KeyValuePair<string, string>("oauth_nonce", nonce));
            parameters.Add(new KeyValuePair<string, string>("oauth_signature_method", method));
            parameters.Add(new KeyValuePair<string, string>("oauth_timestamp", timestamp));
            parameters.Add(new KeyValuePair<string, string>("oauth_token", Info.AccessToken));
        }
    }

    /* OAuth Methods */
    public static string GenerateOAuthHeader(OAuthInfo Info, string Url, List<KeyValuePair<string, string>> postParams = null)
    {
        string version = "1.0";
        string nonce = new Random().Next(0x0000000, 0x7fffffff).ToString("X8") + "laf8a34l3r8j";
        string method = "HMAC-SHA1";
        string timestamp = ((int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString();

        /* Combine oauth info with post params */
        List<KeyValuePair<string, string>> nList = new List<KeyValuePair<string, string>>();
        OAuthInfo.AddOAuthParameters(nList, Info, version, nonce, method, timestamp);
        if (postParams != null) nList.AddRange(postParams);

        /* Encode every key and value */
        var nPostList = nList.Select(x => new KeyValuePair<string, string>(UtilityBelt.EncodeRFC3986(x.Key), UtilityBelt.EncodeRFC3986(x.Value)));

        /* Sort alphabetically and combine into string */
        var nPostString1 = nList.OrderBy(x => x.Key).Select(x => string.Format("{0}={1}", UtilityBelt.EncodeRFC3986(x.Key), UtilityBelt.EncodeRFC3986(x.Value)));
        string nPostString2 = string.Join("&", nPostString1);

        /* Create signature base string */
        string sigBaseString = string.Format("POST&{0}&{1}", UtilityBelt.EncodeRFC3986(Url), UtilityBelt.EncodeRFC3986(nPostString2));

        /* Create signing key */
        string signingString = string.Format("{0}&{1}", UtilityBelt.EncodeRFC3986(Info.ConsumerSecret), UtilityBelt.EncodeRFC3986(Info.AccessSecret));

        /* Calculate signature */
        HMACSHA1 nHasher = new HMACSHA1(Encoding.ASCII.GetBytes(signingString));
        string signature = Convert.ToBase64String(nHasher.ComputeHash(Encoding.ASCII.GetBytes(sigBaseString)));

        /* Create header string and return */
        var headerFormat2 = "OAuth oauth_consumer_key=\"{0}\", oauth_nonce=\"{1}\", " +
                           "oauth_signature=\"{2}\", oauth_signature_method=\"{3}\", " +
                           "oauth_timestamp=\"{4}\", oauth_token=\"{5}\", " +
                           "oauth_version=\"{6}\"";

        var authHeader2 = string.Format(headerFormat2,
                UtilityBelt.EncodeRFC3986(Info.ConsumerKey),
                UtilityBelt.EncodeRFC3986(nonce),
                UtilityBelt.EncodeRFC3986(signature),
                UtilityBelt.EncodeRFC3986(method),
                UtilityBelt.EncodeRFC3986(timestamp),
                UtilityBelt.EncodeRFC3986(Info.AccessToken),
                UtilityBelt.EncodeRFC3986(version)
                );

        return authHeader2;
    }