- Twitter
has announced that they're going to abandon the basic username/password authentication. OAuth seems to be the widely supported authentication standard among many big websites. I have dealed with Yahoo OAuth in the past and to be honest, their documentation is not as good as Twitters. Anyway, in my use case, I won't need to build any fancy UI to ask the users whether they allow my application works on their behalf to access their twitter stream. In other words, I won't need to obtain access tokens because I just want to authorize all my stream requests using OAuth and the access tokens are easily obtained from Twitter dev site. Really good job, Twitter.
Now, given that I have got the following tokens from twitter dev site:
- twitterConsumerKey
- twitterConsumerSecret
- twitterAccessToken
- twitterAccessTokenSecret
What I need to do next is following their
awesome document to create the "Authentication" header. And the most important step to have that is
generating the oauth_signature.
They have said very clear about the parameters included in the request. I want to say another point about these parameters: If you're sending a post request, all the parameter value also have to be url encoded.
My case is quite simple, I'm going to use HttpWebRequest object to send OAuth request to Twitter. POST data will be url encoded and sent once the HttpWebRequest object is created. So I need something to authorise the request, something look like:
public interface IRequestAuthorizer
{
void Authorize(HttpWebRequest request, string postData);
}
And here is the implementation:
public class OAuthRequestAuthorizer : OAuthBase, IRequestAuthorizer
{
private readonly string _twitterConsumerKey;
private readonly string _twitterConsumerSecret;
private readonly string _twitterAccessToken;
private readonly string _twitterAccessTokenSecret;
public OAuthRequestAuthorizer(string twitterConsumerKey, string twitterConsumerSecret, string twitterAccessToken, string twitterAccessTokenSecret)
{
_twitterConsumerKey = twitterConsumerKey;
_twitterConsumerSecret = twitterConsumerSecret;
_twitterAccessToken = twitterAccessToken;
_twitterAccessTokenSecret = twitterAccessTokenSecret;
}
public override string GenerateNonce()
{
return new Nonce().Value;
}
private string BuildHeader(HttpWebRequest request, Uri uri)
{
var nonce = GenerateNonce();
var timeStamp = GenerateTimeStamp();
string normalizedUrl;
string normalizedRequestParameters;
var httpMethod = request.Method;
var signature = GenerateSignature(uri, _twitterConsumerKey, _twitterConsumerSecret, _twitterAccessToken, _twitterAccessTokenSecret,
httpMethod, timeStamp, nonce, out normalizedUrl,
out normalizedRequestParameters);
// https://dev.twitter.com/docs/auth/authorizing-request
return
string.Format(
"OAuth oauth_consumer_key=\"{0}\", " +
"oauth_nonce=\"{1}\", " +
"oauth_signature=\"{2}\", " +
"oauth_signature_method=\"HMAC-SHA1\", " +
"oauth_timestamp=\"{3}\", " +
"oauth_token=\"{4}\", " +
"oauth_version=\"1.0\"",
UrlEncoder.Encode(_twitterConsumerKey),
UrlEncoder.Encode(nonce),
UrlEncoder.Encode(signature),
UrlEncoder.Encode(timeStamp),
UrlEncoder.Encode(_twitterAccessToken));
}
public void Authorize(HttpWebRequest request, string postData)
{
//NOTE: It's a must to collect all param either in the header/querystring or post body
var baseUri = string.IsNullOrEmpty(postData) || request.Method.ToUpper() == "GET"
? request.RequestUri
: new Uri(string.Format("{0}?{1}", request.RequestUri.AbsoluteUri, postData));
request.Headers["Authorization"] = BuildHeader(request, baseUri);
}
}
Nonce is a custom class I created to generate the unique nonce. You can make your self this unique value like a GUID or a DateTime tick. OAuthBase is the base class I got from
oauth.googlecode.com. This class did a good job to generate the signature based on what you provide. So utilizing this object will give you the "Authorization" value to put to the request header. UrlEncoder static class is extracted from the UrlEncode method in OAuthBase class. If you encode the Post data as mentioned above, Twitter stream and API is your oyster like they said :D.
Here is the demo code to consume the twitter stream:
var postData = "track=" + UrlEncoder.Encode("IPhone,IPad");
var requestContentBuffer = Encoding.UTF8.GetBytes(postData);
var request = (HttpWebRequest)WebRequest.Create(new Uri("https://stream.twitter.com/1/statuses/filter.json"));
request.Method = "POST";
request.ContentLength = requestContentBuffer.Length;
request.ContentType = "application/x-www-form-urlencoded";
request.UserAgent = "OAuthTwitterStream";
request.Headers["Accept-Encoding"] = "deflate, gzip";
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
_requestAuthorizer.Authorize(request, postData);
using (var dataStream = request.GetRequestStream())
{
dataStream.Write(requestContentBuffer, 0, requestContentBuffer.Length);
}
var apiResponse = (HttpWebResponse) apiRequest.GetResponse();
apiResponseStream = apiResponse.GetResponseStream();
while(true)
{
var bytesReadCount = apiResponseStream.Read(readBuffer, 0, readBufferSize);
// Parse Json content
}
I didn't expect the job would be as simple as this because lastime when I tried to consume Yahoo OAuth service, It took me a while to debug since a small mistake while encoding for example would give you the no clue Unauthorized error :D.
Cheers