One of the things I really enjoy doing as a developer is integrating with other systems. Not sure why, but its satisfying seeing an external feed of data flowing to where ever I put it. I recently built an OAuth 2 connector for the very excellent RestSharp framework, and decided it might be worthwhile to share this for anyone else doing the same thing.

If you are new to OAuth 2, it generally involves:

  1. Connecting to an auth endpoint and passing in a Consumer Key and Secret
  2. Receiving an Access Token / Bearer Token
  3. Adding the token to all outgoing API requests with an Authorization Header

The Authorization Header looks like:

"Authorization":"Bearer AAAAAAAAAAAAAAcAAAAAAJi59wAAAAAAVrIm3wRBt4TYzuS6RksuulKF99M%3DN2BxQNSDp0r2W2YmSMOgt5ZUoVx6AAMaWxVrsUIHhUE7Z7oHOp"

So the consumer key and secret are only used initially to confirm the client identity, after which the Access Token is used kind of like a session id. All 3 of these pieces of data should be considered sensitive, and you shouldn't expose this in anyway to unauthorized parties.

Using a RESTClient

var consumerKey = "";
var consumerSecret = "";
var accessToken = null;  //This will be null at least the first time
var restClient = new RestClient ("https://api.twitter.com")
{
     Authenticator = new TwitterAuthenticator(consumerKey, consumerSecret, accessToken),
     Encoding = Encoding.UTF8,
     AutomaticDecompression = true,
     Timeout = 60000
};

Next make a call to the API

            string url = $"/1.1/statuses/user_timeline.json?screen_name={_screenName}&count=${_maxItems}";
            var request = new RestRequest(new Uri(url, UriKind.Relative), Method.GET);
            request.AddHeader("Content-Type", "application/json");
            
            var response = _restClient.Execute(request); //Get the raw content OR
            //var response = _restClient.Execute<TwitterResponse>(request); //Get the response as an object
            if (response.StatusCode != HttpStatusCode.OK)
            {
                throw new Exception($"Received {response.StatusCode}:{response.StatusDescription}");
            }
            string content = response.Content;
                ... do something

Ideally you'd want to use .Execute<T>() rather than .Execute(), as the former will deserialize the response into a proper object structure. However you'll need to have those classes defined to match the API response objects. Checkout my other post on Creating a Twitter Connector for how I generated those classes.

TwitterAuthenticator.cs - RestSharp Authenticator

Here's the authentication code:

Source C# -

using System;
using System.IO;
using System.Net;
using System.Security;
using System.Text;
using Broker.Manage.Core;
using Newtonsoft.Json;
using RestSharp;
using RestSharp.Authenticators;

namespace ApiClients
{
    public sealed class TwitterAuthenticator : IAuthenticator
    {
        private SecureString _consumerKey;
        private SecureString _consumerSecret;
        private SecureString _accessToken;
        private CookieContainer _cookieContainer;
        private const string AuthUrl = "https://api.twitter.com/oauth2/token";
        public TwitterAuthenticator(string consumerKey, string consumerSecret, string bearerToken)
        {
            _consumerKey = consumerKey?.ToSecureString();
            _consumerSecret = consumerSecret?.ToSecureString();
            _accessToken = bearerToken?.ToSecureString();
            _cookieContainer = new CookieContainer();
        }

        public void Authenticate(IRestClient client, IRestRequest request)
        {
            if (_accessToken == null || _accessToken.Length == 0)
            {
                PreAuthenticate(client.Proxy);
            }
            request.AddHeader("Authorization", $"Bearer {_accessToken.ToInsecureString()}");
        }
        private void PreAuthenticate(IWebProxy proxy)
        {
            var authRequest = CreateAuthenticationRequest(proxy);
            try
            {
                using (var response = (HttpWebResponse)authRequest.GetResponse())
                {
                    var authResult = GetAuthResponse(response);
                    string accessToken = authResult?.access_token?.Value.ToString();
                    if (string.IsNullOrEmpty(accessToken))
                    {
                        throw new InvalidOperationException("The authentication token received by the server is null or empty");
                    }
                    _accessToken = accessToken.ToSecureString();
                }
            }
            catch (WebException ex)
            {
                var response = (HttpWebResponse)ex.Response;
                if (response != null && response.StatusCode != HttpStatusCode.InternalServerError)
                {
                    using (var data = response.GetResponseStream())
                    {
                        var errorResult = GetObjectFromResponse<dynamic>(data);
                        if (errorResult == null)
                        {
                            throw;
                        }
                        throw new AuthenticationFailedException(errorResult);
                    }
                }
                throw;
            }
        }
        private static dynamic GetAuthResponse(HttpWebResponse response)
        {
            using (var data = response.GetResponseStream())
            {
                if (data == null)
                {
                    throw new InvalidOperationException("The response stream is null when attempting to authenticate");
                }
                return GetObjectFromResponse<dynamic>(data);
            }
        }
        private HttpWebRequest CreateAuthenticationRequest(IWebProxy proxy)
        {
            var key = System.Web.HttpUtility.UrlEncode(_consumerKey.ToInsecureString());
            var secret = System.Web.HttpUtility.UrlEncode(_consumerSecret.ToInsecureString());
            var consumerCredentials = key + ":" + secret;
            var encodedCredentials = Convert.ToBase64String(Encoding.UTF8.GetBytes(consumerCredentials));
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
            var requestUri = new Uri(AuthUrl);
            var request = (HttpWebRequest)WebRequest.Create(requestUri);
            request.Headers.Add("Authorization", $"Basic {encodedCredentials}");
            request.ProtocolVersion = HttpVersion.Version10;
            request.ServicePoint.ConnectionLeaseTimeout = 5000;
            request.ServicePoint.MaxIdleTime = 5000;
            request.ServicePoint.ConnectionLimit = 1;
            request.ContentType = "application/x-www-form-urlencoded;charset=UTF-8";
            request.KeepAlive = false;
            if (proxy != null)
            {
                request.Proxy = proxy;
            }
            request.Method = "POST";
            request.Accept = "application/json;";
            using (var stream = request.GetRequestStream())
            {
                var writer = new StreamWriter(stream);
                writer.Write("grant_type=client_credentials");
                writer.Flush();
            }
            request.AutomaticDecompression = DecompressionMethods.None;
            request.CookieContainer = _cookieContainer;
            return request;
        }
        private static T GetObjectFromResponse<T>(Stream data)
        {
            using (var reader = new StreamReader(data))
            {
                var serialiser = new JsonSerializer();
                using (var jsonTextReader = new JsonTextReader(reader))
                {
                    return serialiser.Deserialize<T>(jsonTextReader);
                }
            }
        }
    }
}