Hi.

On my production server (it works fine on my localhost) when i call https://api.twitter.com/oauth/request_token, the response i receive is a 404 and a page of a Poodle sat on a chair?

I’m using TLS1.2. My token is correct, and so is the Auth header (it works on my local machine after all).

I’m using .Net 4.8 on Server 2012 with IIS 8. The API works on my server when I use Postman (so it isn’t firewall related etc).

How can i debug this? What else can i check? This used to work fine a few weeks ago?

Thanks.

1 Like

Thanks, @MS_Dev. Do you have a code sample we can look at here?

Sure, thanks @jessicagarson .

        public string GetUNIXTimestamp()
        {
            return Convert.ToString((int)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds);
        }
        
        public string EncodeToUpper(string raw)
        {
            raw = HttpUtility.UrlEncode(raw);
            return Regex.Replace(raw, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper());
        }

    public string TwitterAuth()
        {

            string Key = ConfigurationManager.AppSettings["TwitterClientID"];
            string Secret = ConfigurationManager.AppSettings["TwitterClientSecret"];
            string callbackUrl = "https://www.example.co.uk/account/twitterlogincallback";
            string apiUrl = "https://api.twitter.com/oauth/request_token";

            //Add request params for auth header
            Dictionary<string, string> parameters = new Dictionary<string, string>();

            parameters.Add("oauth_consumer_key", Key);
            parameters.Add("oauth_signature_method", "HMAC-SHA1");
            parameters.Add("oauth_timestamp", GetUNIXTimestamp());
            parameters.Add("oauth_nonce", Guid.NewGuid().ToString().Replace("-", ""));
            parameters.Add("oauth_version", "1.0");
            parameters.Add("oauth_callback", callbackUrl);
            
            //order params
            parameters = parameters.OrderBy(x => x.Key).ToDictionary(v => v.Key, v => v.Value);

            string signatureData = "";
            string oAuthHeader = "OAuth ";
            foreach (string keyname in parameters.Keys)
            {
                signatureData += keyname + "=" + parameters[keyname] + "&";
                oAuthHeader += keyname + "=" + "\"" + parameters[keyname] + "\",";             
            }

            //trim last ampersand and last comma
            signatureData = signatureData.Remove(signatureData.Length - 1, 1);
            oAuthHeader = oAuthHeader.Remove(oAuthHeader.Length - 1, 1);

            //join together
            signatureData = "POST&" + EncodeToUpper(apiUrl) + "&" + EncodeToUpper(signatureData);

            //generate HMAC signature
            byte[] content = Encoding.UTF8.GetBytes(signatureData);
            HMACSHA1 hmac = new HMACSHA1(Encoding.ASCII.GetBytes(Secret + "&"));
            byte[] hashbytes = hmac.ComputeHash(content);            
            string hash = Convert.ToBase64String(hashbytes);

            //add hashed signature to header
            oAuthHeader += ",oauth_signature=\"" + EncodeToUpper(hash) + "\"";

            //force tls 1.2
            ServicePointManager.Expect100Continue = true;
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
           
            //fake URL so we can grab params from response easy
            string tempTokenUrl = "https://www.example.com";
            string oauth_token = "";
            string oauth_token_secret = "";
            string oauth_callback_confirmed = "";
            
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl);
            request.Method = "POST";
            request.Headers.Add("Authorization", oAuthHeader);
            request.Host = "api.twitter.com";
            request.Accept = "*/*";
            request.AutomaticDecompression = (DecompressionMethods.GZip | DecompressionMethods.Deflate);
            HttpRequestCachePolicy noCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
            request.CachePolicy = noCachePolicy;
            
            var postData = "oauth_callback=" + EncodeToUpper(callbackUrl);
            var data = Encoding.ASCII.GetBytes(postData);
        
            try
            {
                using (var stream = request.GetRequestStream())
                {
                    stream.Write(data, 0, data.Length);
                }

                HttpWebResponse resp = (HttpWebResponse)request.GetResponse();
                using (StreamReader reader = new StreamReader(resp.GetResponseStream()))
                {
                    string a = reader.ReadToEnd();
                    Uri myUri = new Uri(tempTokenUrl + "?" + a);
                    oauth_token = HttpUtility.ParseQueryString(myUri.Query).Get("oauth_token");
                    oauth_token_secret = HttpUtility.ParseQueryString(myUri.Query).Get("oauth_token_secret");
                    oauth_callback_confirmed = HttpUtility.ParseQueryString(myUri.Query).Get("oauth_callback_confirmed");
                }

            } catch(WebException wex)
            {
                string pageContent = new StreamReader(wex.Response.GetResponseStream()).ReadToEnd();
                log.Info(pageContent);
                log.Info(wex.ToString());
            }

          
            string redirectUrl = "https://api.twitter.com/oauth/authenticate?oauth_token=" + oauth_token;
            return redirectUrl;

        }

Any update @jessicagarson ? Thanks.

Strangely enough (and perhaps reflective of my issue), if i run this PowerShell on my Windows 10 machine, I get a HTTP 401 (expected - unauthorised access).

try {
    Invoke-WebRequest -Headers @{"Authorization" =  'OAuth oauth_consumer_key="xx",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1654005734",oauth_nonce="yy",oauth_version="1.0",oauth_signature="zz"' } `
    -Method POST `
    -Uri https://api.twitter.com/oauth/request_token/
} Catch { 
     write-host $_.Exception.Response.StatusCode.Value__
}

However if i run it on my server, I get HTTP 404!??

Thanks, @MS_Dev. I didn’t see anything in the code sample you sent over, but you may want to double-check your callback URL and authorization information. You may consider using an OAuth library such as this example. Our tools and libraries page may also be helpful here

…so, i wait a week for a generic response and a link to a PHP library. :frowning:

Thanks for your feedback, @MS_Dev. I think the issue might be your callback URL, but I’m not 100% sure.

I suspect it might be to do with it running on Server 2012 (TLS 1.2 support, ciphers, certs? Not sure.). The callback URL works fine on localhost so not convinced it’s that at the moment…

Probably you’ve different callback urls for your localhost & production environment. You’ve to configure them both in your Twitter App configuration.

Which OS (&version) are you running locally? Did you change your code to use TLS 1.2? I just searched for TLS 1.2 and Windows Server 2012, and there are a lot of hits how to enable that.

And for libraries, I’m using LinqToTwitter, maybe you can take a look at that …

1 Like

I get the same with TweetInvi and Microsoft.Owin.Security.Twitter, and also my own implementation above (on Server 2012). It did all work fine up until a few weeks ago.

I’ve got both callback URLs in the app settings.

I also used IISCrypto to check TLS 1.2 is enabled.

I’ll check again tomorrow…

A couple of weeks ago there was this issue with a poodle on a chair, but that issue has been resolved.

Maybe restart the server? Refresh DNS. It’s always the DNS :wink:

2 Likes

Flushed DNS, restarted server. Even tested using CURL…
@jessicagarson - can any devs help with this? I suspect that 404 isn’t entirely accurate…

I’ve finally got it working. It has something to do with Schannel/TLS/Cipher suites: https://www.alkanesolutions.co.uk/2022/06/07/twitter-api-giving-http-404-not-found-when-requesting-a-token/

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.