I'm writing to write a C# method to generate a authentication header for Twitter. I'm trying to search twitter through this API: https://api.twitter.com/1.1/search/tweets.json.
Here's the URL I call:
https://api.twitter.com/1.1/search/tweets.json?q=%23countryman+OR+%23johncooperworks+OR+%40mini%26since_id%3d24012619984051000%26max_id%3d250126199840518145%26result_type%3dmixed%26count%3d4
Here's my method:
private string GetTwitterAuthHeader()
{
const string oauthConsumerKey = "";
const string oauthConsumerSecret = "";
const string oauthToken = "";
const string oauthTokenSecret = "";
const string oauthVersion = "1.0";
const string oauthSignatureMethod = "HMAC-SHA1";
var oauthNonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString(CultureInfo.InvariantCulture)));
var timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var oauthTimestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString(CultureInfo.InvariantCulture);
const string resourceUrl = "https://api.twitter.com/1.1/search/tweets.json";
const string baseFormat = "oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method={2}" +
"&oauth_timestamp={3}&oauth_token={4}&oauth_version={5}";
var baseString = string.Format(baseFormat,
oauthConsumerKey,
oauthNonce,
oauthSignatureMethod,
oauthTimestamp,
oauthToken,
oauthVersion
);
baseString = string.Concat("GET&", Uri.EscapeDataString(resourceUrl), "&", Uri.EscapeDataString(baseString));
var compositeKey = string.Concat(Uri.EscapeDataString(oauthConsumerSecret),
"&", Uri.EscapeDataString(oauthTokenSecret));
string oauthSignature;
using (var hasher = new HMACSHA1(Encoding.ASCII.GetBytes(compositeKey)))
{
oauthSignature = Convert.ToBase64String(
hasher.ComputeHash(Encoding.ASCII.GetBytes(baseString)));
}
const string headerFormat = "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 authHeader = string.Format(headerFormat,
Uri.EscapeDataString(oauthConsumerKey),
Uri.EscapeDataString(oauthNonce),
Uri.EscapeDataString(oauthSignature),
Uri.EscapeDataString(oauthSignatureMethod),
Uri.EscapeDataString(oauthTimestamp),
Uri.EscapeDataString(oauthToken),
Uri.EscapeDataString(oauthVersion)
);
return authHeader;
}
The error I get is:
{
"errors": [
{
"message": "Bad Authentication data",
"code": 215
}
]
}
Any pointers?
Do I need to account for the actual search query in generating the auth header? (e.g. the values I append to the search API)?
I'm finding it hard to debug.
Thanks!
EDIT:
Based on feedback, here's an update:
var resourceUrl = "https://api.twitter.com/1.1/search/tweets.json";
const string baseFormat = "oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method={2}" +
"&oauth_timestamp={3}&oauth_token={4}&oauth_version={5}&q={6}";
var baseString = string.Format(baseFormat,
oauthConsumerKey,
oauthNonce,
oauthSignatureMethod,
oauthTimestamp,
oauthToken,
oauthVersion,
query
);
baseString = string.Concat("GET&",
Uri.EscapeDataString(resourceUrl), "&",
Uri.EscapeDataString(baseString));
Having read https://dev.twitter.com/oauth/overview/creating-signatures closer, this seems to be right. Still, I get the same error.
C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...
It is a bit more cryptic in its style than some other languages, but you get beyond that fairly quickly. C is what is called a compiled language. This means that once you write your C program, you must run it through a C compiler to turn your program into an executable that the computer can run (execute).
C exists everywhere in the modern world. A lot of applications, including Microsoft Windows, run on C. Even Python, one of the most popular languages, was built on C. Modern applications add new features implemented using high-level languages, but a lot of their existing functionalities use C.
The biggest advantage of using C is that it forms the basis for all other programming languages. The mid-level language provides the building blocks of Python, Java, and C++. It's a fundamental programming language that will make it easier for you to learn all other programming languages.
Wrap your "query" in Uri.EscapeDataString()
.
Your exact code above works for me if I do that. That said, you should really be escaping all of those parameter keys and values, and I'm a bit stumped as to why the nonce is working without escaping, since it's base64 encoded. But perhaps I was just lucky in my testing and never hit a nonce with a slash in it.
So, I wrote a class that does this. Let's see how well I can break it down. Let's start with the entire class.
public class TwitterAuthTool : IDisposable {
private const string BASE_AUTH_URL = "https://api.twitter.com/oauth2/token";
private const string BASE_SEARCH_URL = "https://api.twitter.com/1.1/search/tweets.json";
private const string BASE_INVALIDATE_URL = "https://api.twitter.com/oauth2/invalidate_token";
private AccessToken Credentials;
private string BearerTokenCredentials;
public TwitterAuthTool( string p_ConsumerKey, string p_ConsumerSecret ) {
BearerTokenCredentials =
Convert.ToBase64String(
Encoding.ASCII.GetBytes(
string.Format( "{0}:{1}",
Uri.EscapeUriString( p_ConsumerKey ),
Uri.EscapeUriString( p_ConsumerSecret ) ) ) );
HttpWebRequest _Request = HttpWebRequest.Create( BASE_AUTH_URL ) as HttpWebRequest;
_Request.KeepAlive = false;
_Request.Method = "POST";
_Request.Headers.Add( "Authorization", string.Format( "Basic {0}", BearerTokenCredentials ) );
_Request.ContentType = "application/x-www-form-urlencoded;charset=UTF-8";
byte[] _Content = Encoding.ASCII.GetBytes( "grant_type=client_credentials" );
using( Stream _Stream = _Request.GetRequestStream() )
_Stream.Write( _Content, 0, _Content.Length );
HttpWebResponse _Response = _Request.GetResponse() as HttpWebResponse;
DataContractJsonSerializer _AccessTokenJsonSerializer = new DataContractJsonSerializer( typeof( AccessToken ) );
Credentials = (AccessToken)_AccessTokenJsonSerializer.ReadObject( _Response.GetResponseStream() );
}
public List<Tweet> GetLatest( string p_Query, int p_Count = 100 ) {
TwitterResults _TwitterResults;
List<Tweet> _ReturnValue = new List<Tweet>();
DataContractJsonSerializer _JsonSerializer = new DataContractJsonSerializer( typeof( TwitterResults ) );
HttpWebRequest _Request = WebRequest.Create( string.Format( "{0}?q={1}&result_type=recent&count={2}", BASE_SEARCH_URL, p_Query, p_Count ) ) as HttpWebRequest;
_Request.Headers.Add( "Authorization", string.Format( "Bearer {0}", Credentials.access_token ) );
_Request.KeepAlive = false;
_Request.Method = "GET";
HttpWebResponse _Response = _Request.GetResponse() as HttpWebResponse;
_TwitterResults = (TwitterResults)_JsonSerializer.ReadObject( _Response.GetResponseStream() );
_ReturnValue.AddRange( _TwitterResults.statuses );
while( !string.IsNullOrWhiteSpace( _TwitterResults.search_metadata.next_results ) ) {
_Request = WebRequest.Create( string.Format( "{0}{1}", BASE_SEARCH_URL, _TwitterResults.search_metadata.next_results ) ) as HttpWebRequest;
_Request.Headers.Add( "Authorization", string.Format( "Bearer {0}", Credentials.access_token ) );
_Request.KeepAlive = false;
_Request.Method = "GET";
_Response = _Request.GetResponse() as HttpWebResponse;
_TwitterResults = (TwitterResults)_JsonSerializer.ReadObject( _Response.GetResponseStream() );
_ReturnValue.AddRange( _TwitterResults.statuses );
}
return _ReturnValue;
}
public List<Tweet> GetLatestSince( string p_Query, long p_SinceId, int p_Count = 100 ) {
TwitterResults _TwitterResults;
List<Tweet> _ReturnValue = new List<Tweet>();
DataContractJsonSerializer _JsonSerializer = new DataContractJsonSerializer( typeof( TwitterResults ) );
HttpWebRequest _Request = WebRequest.Create( string.Format( "{0}?q={1}&result_type=recent&count={2}&since_id={3}", BASE_SEARCH_URL, p_Query, p_Count, p_SinceId ) ) as HttpWebRequest;
_Request.Headers.Add( "Authorization", string.Format( "Bearer {0}", Credentials.access_token ) );
_Request.KeepAlive = false;
_Request.Method = "GET";
HttpWebResponse _Response = _Request.GetResponse() as HttpWebResponse;
_TwitterResults = (TwitterResults)_JsonSerializer.ReadObject( _Response.GetResponseStream() );
_ReturnValue.AddRange( _TwitterResults.statuses );
while( !string.IsNullOrWhiteSpace( _TwitterResults.search_metadata.next_results ) ) {
_Request = WebRequest.Create( string.Format( "{0}{1}", BASE_SEARCH_URL, _TwitterResults.search_metadata.next_results ) ) as HttpWebRequest;
_Request.Headers.Add( "Authorization", string.Format( "Bearer {0}", Credentials.access_token ) );
_Request.KeepAlive = false;
_Request.Method = "GET";
_Response = _Request.GetResponse() as HttpWebResponse;
_TwitterResults = (TwitterResults)_JsonSerializer.ReadObject( _Response.GetResponseStream() );
_ReturnValue.AddRange( _TwitterResults.statuses );
}
return _ReturnValue;
}
public void Dispose() {
HttpWebRequest _Request = WebRequest.Create( BASE_INVALIDATE_URL ) as HttpWebRequest;
_Request.KeepAlive = false;
_Request.Method = "POST";
_Request.Headers.Add( "Authorization", string.Format( "Basic {0}", BearerTokenCredentials ) );
_Request.ContentType = "application/x-www-form-urlencoded";
byte[] _Content = Encoding.ASCII.GetBytes( string.Format( "access_token={0}", Credentials.access_token ) );
using( Stream _Stream = _Request.GetRequestStream() )
_Stream.Write( _Content, 0, _Content.Length );
try {
_Request.GetResponse();
} catch {
// The bearer token will time out if this fails.
}
}
}
Twitter uses something OAuth2-ish. You have to login before you do ANYTHING else. This is what the constructor of the above class does. When you do that, you get back an access token. It's a simple little JSON object that easily deserializes into the following little class.
[DataContract]
public class AccessToken {
[DataMember]
public string token_type;
[DataMember]
public string access_token;
}
Once you have the correct credentials, you use THOSE to access things. Unfortunately, the JSON objects you get back from a query are huge. I had to create 17 different classes to deserialize the entirety of it. I'll look at putting that library up on github.
As you see in the Dispose, I don't care if the auth token fails. The credentials they give you time out after 8 hours.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With