Rolling my own OAuth 1.0 client

by dotnetnerd 24. March 2020 08:18

OAuth is one of those things where I always wonder about how had the state of libraries etc are. It seems like a problem pretty much everyone will tackle, but I am still not able to find a simple, library that works well with .NET Core HttpClient without being a whole framework in itself or at least very framework dependent. Recently I needed just that for OAuth 1.0a, but ended up rolling my own. It is just the very basics, but I wanted to put it out there, because I guess others might have use for it, or I will some time in the future.

public class OAuth1
{
    private static string validChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    private static Random random = new Random();

    public string SignatureBase(string method, string url, Dictionary<string, string> oauthParams)
    {           
        return method + "&" + Uri.EscapeDataString(url) + "&" + Uri.EscapeDataString(string.Join("&", oauthParams.OrderBy(p => p.Key).Select(p => $"{p.Key}={p.Value}")));
    }

    public string Signature(string consumerSecret, string tokenSecret, string signatureBase)
    {
        var encoding = new ASCIIEncoding();
        string key = Uri.EscapeDataString(consumerSecret) + "&" + (string.IsNullOrEmpty(tokenSecret) ? "" : Uri.EscapeDataString(tokenSecret));
        byte[] keyBytes = encoding.GetBytes(key);
        byte[] messageBytes = encoding.GetBytes(signatureBase);

        using (HMACSHA1 SHA1 = new HMACSHA1(keyBytes))
        {
            var hashed = SHA1.ComputeHash(messageBytes);
            return Convert.ToBase64String(hashed);
        }
    }

    public string OAuthHeader(Dictionary<string, string> oauthParams)
    {
        return "OAuth " + string.Join(", ", oauthParams.Select(p => [email protected]"{p.Key}=""{p.Value}"""));
    }

    public string GenerateTimeStamp()
    {
         TimeSpan ts = DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0));
        return ((int)Math.Floor(ts.TotalSeconds)).ToString();
    }
   
    public string Nonce(int length = 32)
    {
        var nonceString = new StringBuilder();
        for (int i = 0; i < length; i++)
        {
            nonceString.Append(validChars[random.Next(0, validChars.Length - 1)]);
         }

        return nonceString.ToString();
    }

    public async Task<string> Request(string url, IDictionary<string, string> otherParams, string oAuthHeader)
    {
        using (var httpClient = new HttpClient())
        {
            httpClient.DefaultRequestHeaders.Add("Authorization", oAuthHeader);

            string fullUrl = otherParams.Any() ? (url + "?" + string.Join("&", otherParams.Select(p => $"{p.Key}={p.Value}"))) : url;

            HttpResponseMessage httpResp = await httpClient.GetAsync(fullUrl);

            return await httpResp.Content.ReadAsStringAsync();
        }
     }
}

With this in place we can do requests with the correct headers like this.

string _consumerKey = "aaa";
string _consumerSecret = "bbb";
string _accessToken = "ccc";
string _accessTokenSecret = "ddd";

var oAuth = new OAuth1();

string url = "http://myfancysite.com/api/cars/register";

string timestamp = oAuth.GenerateTimeStamp();
  string nonce = oAuth.Nonce();

Dictionary<string, string> oAuthParams = new Dictionary<string, string>()
     {
         { "oauth_consumer_key", _consumerKey },
         { "oauth_nonce", nonce },
         { "oauth_signature_method", "HMAC-SHA1" },
          { "oauth_timestamp",  timestamp.ToString()},
         { "oauth_token", _accessToken },
         { "oauth_version",  "1.0"},
     };

Dictionary<string, string> otherParams = new Dictionary<string, string>()
     {
         { "email", "[email protected]" },
         { "make", "Skoda" },
         { "model", "Fabia" },
         { "firstname", "Johnny" },
         { "lastname", "Madsen" }
    };

string signatureBase = oAuth.SignatureBase("GET", url, oAuthParams.Concat(otherParams.Select(p => new KeyValuePair<string, string>(p.Key, Uri.EscapeDataString(p.Value)))).ToDictionary(p => p.Key, p => p.Value));
 
  string signature = oAuth.Signature(_consumerSecret, _accessTokenSecret, signatureBase);
 

oAuthParams.Add("oauth_signature", signature);

string oAuthHeader = oAuth.OAuthHeader(oAuthParams);

var result = await oAuth.Request(url, otherParams, oAuthHeader);

Who am I?

My name is Christian Holm Diget, and I work as an independent consultant, in Denmark, where I write code, give advice on architecture and help with training. On the side I get to do a bit of speaking and help with miscellaneous community events.

Some of my primary focus areas are code quality, programming languages and using new technologies to provide value.

Microsoft Certified Professional Developer

Microsoft Most Valuable Professional

Month List

halk tv