Tweeting works but sending direct messages gives me a 410, authorization failed


#1

Hi I’m trying to write a powershell tool to tweet and send messages.

I can send tweets without a problem but when I try to send private messages i get a 401, as in i can’t be authorized but it’s the exact same details that works with tweeting. I’ve also made sure to set rights to read, write and direct messages.

What could be wrong?

    [Reflection.Assembly]::LoadWithPartialName("System.Security")  
    [Reflection.Assembly]::LoadWithPartialName("System.Net")  
  
        $screen_name = [System.Uri]::EscapeDataString("twittb0t");  
   $text = [System.Uri]::EscapeDataString("hithere");

    $oauth_consumer_key = "<redacted>";  
    $oauth_consumer_secret = "<redacted>";  
    $oauth_token = "<redacted>";  
    $oauth_token_secret = "<redacted>";  
    $oauth_nonce = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes([System.DateTime]::Now.Ticks.ToString()));  
    $ts = [System.DateTime]::UtcNow - [System.DateTime]::ParseExact("01/01/1970", "dd/MM/yyyy", $null).ToUniversalTime();  


    $oauth_timestamp = [System.Convert]::ToInt64($ts.TotalSeconds).ToString();  
      
    $signature = "POST&";  
    $signature += [System.Uri]::EscapeDataString("https://api.twitter.com/1.1/direct_messages/new.json") + "&";    
    $signature += [System.Uri]::EscapeDataString("oauth_consumer_key=" + $oauth_consumer_key + "&");  
    $signature += [System.Uri]::EscapeDataString("oauth_nonce=" + $oauth_nonce + "&");   
    $signature += [System.Uri]::EscapeDataString("oauth_signature_method=HMAC-SHA1&");  
    $signature += [System.Uri]::EscapeDataString("oauth_timestamp=" + $oauth_timestamp + "&");  
    $signature += [System.Uri]::EscapeDataString("oauth_token=" + $oauth_token + "&");  
    $signature += [System.Uri]::EscapeDataString("oauth_version=1.0&");  
    $signature += [System.Uri]::EscapeDataString("text=" + $text);  
    $signature += [System.Uri]::EscapeDataString("&screen_name=" + $screen_name);
    $signature_key = [System.Uri]::EscapeDataString($oauth_consumer_secret) + "&" + [System.Uri]::EscapeDataString($oauth_token_secret);  
      
    $hmacsha1 = new-object System.Security.Cryptography.HMACSHA1;  
    $hmacsha1.Key = [System.Text.Encoding]::ASCII.GetBytes($signature_key);  
    $oauth_signature = [System.Convert]::ToBase64String($hmacsha1.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($signature)));  
      
    $oauth_authorization = 'OAuth ';  
    $oauth_authorization += 'oauth_consumer_key="' + [System.Uri]::EscapeDataString($oauth_consumer_key) + '",';  
    $oauth_authorization += 'oauth_nonce="' + [System.Uri]::EscapeDataString($oauth_nonce) + '",';  
    $oauth_authorization += 'oauth_signature="' + [System.Uri]::EscapeDataString($oauth_signature) + '",';  
    $oauth_authorization += 'oauth_signature_method="HMAC-SHA1",'  
    $oauth_authorization += 'oauth_timestamp="' + [System.Uri]::EscapeDataString($oauth_timestamp) + '",'  
    $oauth_authorization += 'oauth_token="' + [System.Uri]::EscapeDataString($oauth_token) + '",';  
    $oauth_authorization += 'oauth_version="1.0"';  
      
    
    $post_body = [System.Text.Encoding]::ASCII.GetBytes("text=" + $text + "&screen_name=" + $screen_name);   
    [System.Net.HttpWebRequest] $request = [System.Net.WebRequest]::Create("https://api.twitter.com/1.1/direct_messages/new.json");  
    $request.Method = "POST";  
    $request.Headers.Add("Authorization", $oauth_authorization);  
    $request.ContentType = "application/x-www-form-urlencoded";  
    $request.ContentLength = $post_body.Length

    $body = $request.GetRequestStream();  
    $body.write($post_body, 0, $post_body.Length);  
    $body.flush();  
    $body.close();  
    $response = $request.GetResponse();  
    $response.Close()

#2

At first glance, it looks like you aren’t ordering your parameters when building the signature base string – you have the screen_name parameter coming after the text parameter.


#3

Wow, thanks for pointing that out, that solved it! I quickly glanced over the API and looked at the POST data POST Data text=hello%2C%20tworld.%20welcome%20to%201.1.&screen_name=theseancook

And I wrongfully assumed that was how you built it. Thank you so much!


#4

Great. I recommend that you make the “sorting” aspect of OAuth a more distinct function you’re leveraging, rather than trying to build the concatenated string by hand.


#5

Ok great, I’ll that apply that to my code. Thanks for the advice :slight_smile:


#6

I forgot to ask and I hope I’m not out of line to ask it here but are there any limitations to how many IPs that can use the same auth token? Like can two accounts use the same auth token to do tasks? Also if I understood the documentation right, they would share the 350 rate limited requests per hour? Thanks in advance!


#7

The same API keys and same access tokens can be used across many different IP addresses. API v1, which is soon to be retired, has a rate limiting model that would give you 350 authenticated requests per user per IP address. In API v1.1, it’s a little more complicated of a model, with each endpoint having its own limit.


#8

I’ve noticed an error and I can’t really figure out what I’m doing wrong. Namely, sending data that is only a-Z 0-9 is fine but when I use special characters like !() I will get a 401 authentication fail. I assume that I’m processing my POST data correctly by escaping it. Any ideas?

EDIT: Okay apparently it the escape function didn’t cover everything, I just added replacement of the characters it was missing and it works fine.


#9

I removed “.ToUniversalTime()” from the following line:
$ts = [System.DateTime]::UtcNow - [System.DateTime]::ParseExact(“01/01/1970”, “dd/MM/yyyy”, $null).ToUniversalTime();

That resolved the 401 error.