Using TwitPic API 2.0 (OAuth Echo) from a C# client to upload pictures

Last week we enjoyed a remote pair programming session with my friend Johnny, the challenge was to upload a picture from a console application using the new TwitPic's API which takes advantage of the OAuth Echo authentication mechanism provided by Twitter.

The scenario we wanted to create wasn't a web application, so we had not been able to handle a full OAuth process which forced us to perform an oob authentication method as described here:

"For applications that really can't handle the full OAuth process Twitter provides the out-of-band/PIN code authentication mode, also known as oob.
This authentication flow is almost identical to full OAuth except instead of being directed back to your website the user is presented with a PIN code. The user is then asked to type this PIN code into your application which will then complete the token exchange."

Below you will find every step we followed to get this running.

Getting a token and a token secret from Twitter

  1. Register your application on http://dev.twitter.com/ to get your Consumer Key and Consumer Secret
     image
  2. Create a new console application and reference the DotNetOpenAuth (download it from here), System.Web and System.Windows.Forms assemblies
    image
  3. Create a service descriptor providing Twitter endpoints:
    var descriptor = new ServiceProviderDescription
                    {
                        RequestTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/request_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
                        UserAuthorizationEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/authenticate", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
                        AccessTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/access_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
                        TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() }
                    };
  4. Now, we must create a token manager that implements IConsumerTokenManager and IOpenIdOAuthTokenManager interfaces. In this case we used an in memory implementation provided as part of DotNetOpenAuth samples. You can find the code in DotNetOpenAuth.ApplicationBlock sample, or you can copy and paste it form here:
    namespace TwitpicOAuthClient
    {
        using System;
        using System.Collections.Generic;
        using DotNetOpenAuth.OAuth.ChannelElements;
        using DotNetOpenAuth.OAuth.Messages;
        using DotNetOpenAuth.OpenId.Extensions.OAuth;
     
        internal class InMemoryTokenManager : IConsumerTokenManager, IOpenIdOAuthTokenManager
        {
            private readonly Dictionary<string, string> tokensAndSecrets = new Dictionary<string, string>();
     
            public InMemoryTokenManager(string consumerKey, string consumerSecret)
            {
                if (String.IsNullOrEmpty(consumerKey))
                {
                    throw new ArgumentNullException("consumerKey");
                }
     
                this.ConsumerKey = consumerKey;
                this.ConsumerSecret = consumerSecret;
            }
     
            public string ConsumerKey { get; private set; }
     
            public string ConsumerSecret { get; private set; }
     
            public string GetTokenSecret(string token)
            {
                return this.tokensAndSecrets[token];
            }
     
            public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response)
            {
                this.tokensAndSecrets[response.Token] = response.TokenSecret;
            }
     
            public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret)
            {
                this.tokensAndSecrets.Remove(requestToken);
                this.tokensAndSecrets[accessToken] = accessTokenSecret;
            }
     
            public TokenType GetTokenType(string token)
            {
                throw new NotImplementedException();
            }
     
            public void StoreOpenIdAuthorizedRequestToken(string consumerKey, AuthorizationApprovedResponse authorization)
            {
                this.tokensAndSecrets[authorization.RequestToken] = String.Empty;
            }
        }
    }
  5. Provide the consumer information you got when you registered your application on Twitter. At this point you should be able to get a token URL from Twitter and let the user know from where he can authenticate to get the PIN code.
    string requestToken;
    var twitterConsumerKey = "twitter_consumer_key";
    var twitterConsumerSecret = "twitter_consumer_key_secret";
     
    var consumer = new DesktopConsumer(descriptor, new InMemoryTokenManager(twitterConsumerKey, twitterConsumerSecret));
    var requestTokenUrl = consumer.RequestUserAuthorization(null, null, out requestToken);
     
    Console.WriteLine("Please copy the following Url to your favorite web browser (it's already on your clipboard), and write the PIN below:");
    Console.WriteLine(requestTokenUrl);
    Clipboard.SetText(requestTokenUrl.ToString());
     
    Console.Write("PIN: ");
    var pin = Console.ReadLine();
  6. Decorate the Main method with the [STAThread] attribute to avoid a ThreadStateException while copying the URL to the user's clipboard:
    [STAThread]
    public static void Main(string[] args)
    {
  7. Get the access_token and access_token_secret values provided by twitter:
    var response = consumer.ProcessUserAuthorization(requestToken, pin);
    var token = response.AccessToken;
    var tokenSecret = ((ITokenSecretContainingMessage)response).TokenSecret;

Using TwitPic API 2.0

  1. First of all we must get an API key from http://dev.twitpic.com/apps/new

    image 

  2. Further on this guide we will need to sign Twitpic's requests, to do that you have to include the OAuthBase.cs library on your project(http://oauth.googlecode.com/svn/code/csharp/OAuthBase.cs). 
  3. Provide your Twitpic API key and define the following variables just after where you got the token and the token secret from Twitter:
    var twitpicApiKey = "twitpic_api_key";
    var oauthSignaturePattern = "OAuth realm=\"{0}\", oauth_consumer_key=\"{1}\", oauth_signature_method=\"HMAC-SHA1\", oauth_token=\"{2}\", oauth_timestamp=\"{3}\", oauth_nonce=\"{4}\", oauth_version=\"1.0\", oauth_signature=\"{5}\"";
    var authenticationRealm = "http://api.twitter.com/";
    var twitpicUploadApiUrl = "http://api.twitpic.com/2/upload.json";
    var twitterVerifyCredentialsApiUrl = "https://api.twitter.com/1/account/verify_credentials.json";
    var contentEncoding = "iso-8859-1";
  4. Use the OAuthBase library to generate the signature:
    var oauth = new OAuthBase();
    string normalizedString, normalizedParameters;
    var timestamp = oauth.GenerateTimeStamp();
    var nounce = oauth.GenerateNonce();
    var signature = oauth.GenerateSignature(
                        new Uri(twitterVerifyCredentialsApiUrl),
                        twitterConsumerKey,
                        twitterConsumerSecret,
                        token,
                        tokenSecret,
                        "GET",
                        timestamp,
                        nounce,
                        out normalizedString,
                        out normalizedParameters);
     
    signature = HttpUtility.UrlEncode(signature);
  5. Create a multipart/form-data HttpWebRequest and set the required X-Verify-Credentials-Authorization and X-Auth-Service-Provider headers as described in http://dev.twitpic.com/docs/2/upload/ page:
    var boundary = Guid.NewGuid().ToString();
    var request = (HttpWebRequest)WebRequest.Create(twitpicUploadApiUrl);
     
    request.PreAuthenticate = true;
    request.AllowWriteStreamBuffering = true;
    request.ContentType = string.Format("multipart/form-data; boundary={0}", boundary);
     
    request.Headers.Add("X-Auth-Service-Provider", twitterVerifyCredentialsApiUrl);
     
    var authorizationHeader = string.Format(
                                CultureInfo.InvariantCulture,
                                oauthSignaturePattern,
                                authenticationRealm,
                                twitterConsumerKey,
                                token,
                                timestamp,
                                nounce,
                                signature);
    request.Headers.Add("X-Verify-Credentials-Authorization", authorizationHeader);
     
    request.Method = "POST";
  6. Create the payload for the request, which includes the TwitPic message and image contents:
    var header = string.Format("--{0}", boundary);
    var footer = string.Format("--{0}--", boundary);
     
    var contents = new StringBuilder();
    contents.AppendLine(header);
     
    string fileContentType = "image/png";
    string fileHeader = string.Format("Content-Disposition: file; name=\"{0}\"; filename=\"{1}\"", "media", "sample_image.png");
    string fileData = Encoding.GetEncoding(contentEncoding).GetString(File.ReadAllBytes(@"c:\sample_image.png"));
     
    contents.AppendLine(fileHeader);
    contents.AppendLine(string.Format("Content-Type: {0}", fileContentType));
    contents.AppendLine();
    contents.AppendLine(fileData);
     
    contents.AppendLine(header);
    contents.AppendLine(string.Format("Content-Disposition: form-data; name=\"{0}\"", "key"));
    contents.AppendLine();
    contents.AppendLine(twitpicApiKey);
     
    contents.AppendLine(header);
    contents.AppendLine(String.Format("Content-Disposition: form-data; name=\"{0}\"", "message"));
    contents.AppendLine();
    contents.AppendLine("Testing Twitpic API" + Path.GetTempFileName()); // GetTempFileName is to avoid duplicate prevention.
     
    contents.AppendLine(footer);
     
    byte[] bytes = Encoding.GetEncoding(contentEncoding).GetBytes(contents.ToString());
    request.ContentLength = bytes.Length;
  7. And finally perform the request:
    using (var requestStream = request.GetRequestStream())
    {
        requestStream.Write(bytes, 0, bytes.Length);
     
        using (var twitpicResponse = (HttpWebResponse)request.GetResponse())
        {
            using (var reader = new StreamReader(twitpicResponse.GetResponseStream()))
            {
                Console.WriteLine(twitpicResponse.StatusCode + ": " + reader.ReadToEnd());
                Console.Read();
            }
        }
    }

Running the Sample

  1. Run the console application and the token URL should be copied to your clipboard: image
  2. Open a web browser an paste the URL to obtain the PIN code:

    image
  3. Enter the PIN code in the console and if there are no errors you will be able to view the new image on Twitpic:

    image

Published: July 31 2010

  • category:
blog comments powered by Disqus