Search code examples
c#apioauthvimeo

c# OAuth and Signatures


I am trying to use the Vimeo API and I have been having some problems generating the signature....

Basically I have this class

public class OAuthParameters
{
    public string RedirectUrl { get; set; }

    public string ClientId { get; set; }
    public string ClientSecret { get; set; }

    protected string NormalizeParameters(SortedDictionary<string, string> parameters)
    {
        StringBuilder sb = new StringBuilder();

        var i = 0;
        foreach (var parameter in parameters)
        {
            if (i > 0)
                sb.Append("&");

            sb.AppendFormat("{0}={1}", parameter.Key, parameter.Value);

            i++;
        }

        return sb.ToString();
    }

    private string GenerateBase(string nonce, string timeStamp, Uri url)
    {
        var parameters = new SortedDictionary<string, string>
        {
            {"oauth_consumer_key", ClientId},
            {"oauth_signature_method", "HMAC-SHA1"},
            {"oauth_timestamp", timeStamp},
            {"oauth_nonce", nonce},
            {"oauth_version", "1.0"}
        };

        var sb = new StringBuilder();
        sb.Append("GET");
        sb.Append("&" + Uri.EscapeDataString(url.AbsoluteUri));
        sb.Append("&" + Uri.EscapeDataString(NormalizeParameters(parameters)));
        return sb.ToString();
    }

    public string GenerateSignature(string nonce, string timeStamp, Uri url)
    {
        var signatureBase = GenerateBase(nonce, timeStamp, url);
        var signatureKey = string.Format("{0}&{1}", ClientId, "");
        var hmac = new HMACSHA1(Encoding.ASCII.GetBytes(signatureKey));
        return Convert.ToBase64String(hmac.ComputeHash(new ASCIIEncoding().GetBytes(signatureBase)));
    }
}

The code that calls this is in another class and it has this method

public string GetAuthorizationUrl(string Url)
{
    var sb = new StringBuilder();
    var nonce = Guid.NewGuid().ToString();
    var timeStamp = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds.ToString();
    var signature = parameters.GenerateSignature(nonce, timeStamp, new Uri(Url));

    sb.Append(GenerateQueryStringOperator(sb.ToString()) + "oauth_consumer_key=" + Uri.EscapeDataString(parameters.ClientId));
    sb.Append("&oauth_nonce=" + Uri.EscapeDataString(nonce));
    sb.Append("&oauth_timestamp=" + Uri.EscapeDataString(timeStamp));
    sb.Append("&oauth_signature_method=" + Uri.EscapeDataString("HMAC-SHA1"));
    sb.Append("&oauth_version=" + Uri.EscapeDataString("1.0"));
    sb.Append("&oauth_signature=" + Uri.EscapeDataString(signature));

    return Url + sb.ToString();
}

private string GenerateQueryStringOperator(string currentUrl)
{
    if (currentUrl.Contains("?"))
        return "&";
    else
        return "?";
}

the Url I am passing (to get my auth token) is https://vimeo.com/oauth/request_token but whenever I call this I get an error stating

401 Unauthorized - Invalid signature - The oauth_signature passed was not valid.

This is driving me nuts. I can't see what could be causing the issue.

If anyone can give me some help, that would be great.

Update 1

Someone stated that using Uri.EscapeDataString was not the correct way to encode my strings. I was using this function before I moved the EscapeDataString:

    protected string UrlEncode(string unencodedString)
    {
        var encodedString = new StringBuilder();
        var unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";

        foreach (char symbol in unencodedString)
            if (unreservedChars.IndexOf(symbol) != -1)
                encodedString.Append(symbol);
            else
                encodedString.Append('%' + String.Format("{0:X2}", (int)symbol));

        return encodedString.ToString();
    }

but this still not help. Also using http://hueniverse.com/oauth/guide/authentication/ did not provide any insights into what is causing my issue :(

Update 2

So, I ran the code through hueniverse and found that my timestamp was not exact (it had decimal places) so I changed the way I generate my time stamp using this function:

    public string GenerateTimeStamp()
    {
        TimeSpan ts = DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0));
        string timeStamp = ts.TotalSeconds.ToString();
        timeStamp = timeStamp.Substring(0, timeStamp.IndexOf("."));
        return timeStamp;
    }

So now when I look at my base signature I get:

POST&https%3A%2F%2Fvimeo.com%2Foauth%2Frequest_token%2F&oauth_consumer_key%3Dmykey%26oauth_nonce%3D66c80d6b-9ff6-404b-981b-56f40f356a31%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1370444366%26oauth_token%3D%26oauth_version%3D1.0

and if I compare it to the one generated from hueniverse, it generates:

POST&https%3A%2F%2Fvimeo.com%2Foauth%2Frequest_token%2F&oauth_consumer_key%3Dmykey%26oauth_nonce%3D00b81872-688b-4184-a978-206e5fbcc531%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1370446860%26oauth_token%3D%26oauth_version%3D1.0

If the length of the generated strings are anything to go by, then they are exact. My next test is to create it exactly the same timestamp and all to see if they generate the same signature....


Solution

  • The problem with the code is here.

    In the GenerateSignature function, you are setting variable:

    var signatureKey = string.Format("{0}&{1}", ClientId, "");
    

    This should be:

    var signatureKey = string.Format("{0}&{1}", ClientSecret , "");