Search code examples
c#.netdotnetopenauthnetflix

How do I use DotNetOpenAuth with the Netflix API?


This is what I have so far, but it isn't working as I don't understand how DotNetOpenAuth is supposed to work. I only need it to sign the outcome with my key, but I am not having luck. Everything seems to point towards me needing to get the client to authorize my access, but I just need to get it signed as I don't need the user for this request.

Refer to http://developer.netflix.com/docs/read/Security , the section labeled "Netflix API Requests"

public class class1
{
    private void Main()
    {
        string consumerKey = "<MyAPIKey>";
        string consumerSecret = "<MyAPISharedSecret>";
        var tokenManager = new InMemoryTokenManager(consumerKey, consumerSecret);

        MessageReceivingEndpoint oauthEndpoint =
            new MessageReceivingEndpoint(new Uri("http://api-public.netflix.com/catalog/titles/index"),
                                         HttpDeliveryMethods.PostRequest);
        WebConsumer consumer = new WebConsumer(
            new ServiceProviderDescription
                {
                    RequestTokenEndpoint = oauthEndpoint,
                    UserAuthorizationEndpoint = oauthEndpoint,
                    AccessTokenEndpoint = oauthEndpoint,
                    TamperProtectionElements =
                        new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement()},
                },
            tokenManager);


        var result = consumer.Channel.Request(new AccessProtectedResourceRequest());


    }

    internal class InMemoryTokenManager : IConsumerTokenManager
    {
        private 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();
        }
    }
}

Solution

  • Your actual question should be something like 'Is it possible to use DotNetOpenAuth to sign requests with or without access token?", to answer that question I should say I don't know and even I can't find it out by reading DotNetOpenAuth codebase.

    There is no single page of documentation available for DotNetOpenAuth and the codebase is so huge that you can't read it and understand what is supported by it or not.

    I guess making non-authenticated request is not an issue as it is simply a query string parameter added to your request.

    But to make signed requests you need to follow a simple process:

    1. Collecting request parameters
    2. Calculating signature
    3. Making request(signed/protected)

    Collecting request parameters

    These are basically two categories of parameters, oauth specific parameters and Netflix API specific parameters.

    Among the OAuth specific parameters is nonce, this is the code in which you can use to generate a nonce value:

    public static string GenerateNonce()
    {
        byte[] bytes = new byte[32];
        var first = Guid.NewGuid().ToByteArray();
        var second = Guid.NewGuid().ToByteArray();
        for (var i = 0; i < 16; i++)
            bytes[i] = first[i];
        for (var i = 16; i < 32; i++)
            bytes[i] = second[i - 16];
        var result = Convert.ToBase64String(bytes, Base64FormattingOptions.None);
        result = new string(result.ToCharArray().Where(char.IsLetter).ToArray());
        return result;
    }
    

    And another OAuth specific parameter is timestamp, this is the code in which you can use to calculate timestamp:

    DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds
    

    Other oauth specific parameters are easy to provision and no need to write a specific code for them.

    API specific parameters are any value you add to query string or to the authorization headers(except the oauth_signature itself) or to the body request(if request content type is application/x-www-form-urlencoded).

    Calculating signature

    To make either a signed request or a protected signature you need to calculate a signature, which the process is almost the same, except the way that you create signing key:

    1. Calculate signature base string
    2. Calculate signing key
    3. Creating the signature by signing the signature base string using signing key

    To calculate signature base string you need to first concatenate all parameters into a string and the percent encode the whole string. This is the code which helps you doing percent encoding:

    public static string Encode(string source)
    {
        Func<char, string> encodeCharacter = c => {
            if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '.' || c == '-' || c == '_' || c == '~'))
                return new string(c, 1);
            return EncodeCharacter(c);
        };
        return string.Concat(source.ToCharArray().Select(encodeCharacter));
    }
    

    Also you need to sort parameters in alphabetical order and be concatenated using '&'. Here is the code which you may have to write to do this:

    public static string CalculateParameterString(KeyValuePair<string, string>[] parameters)
    {
        var q = from entry in parameters
                let encodedkey = PercentEncode.Encode(entry.Key)
                let encodedValue = PercentEncode.Encode(entry.Value)
                let encodedEntry = encodedkey + "=" + encodedValue
                orderby encodedEntry
                select encodedEntry;
        var result = string.Join("&", q.ToArray());
        return result;
    }
    

    Lets call the above string 'parameters string'. Then to calculate signature base string all you need is to concatenate http verb of your request, your request's url and parameters string together using '&'. Also you need to percent encode them first. Here is the code which does that:

    public static string CalcualteSignatureBaseString(string httpMethod, string baseUri, string parametersString)
    {
        return httpMethod.ToUpper() + "&" + PercentEncode.Encode(baseUri) + "&" + PercentEncode.Encode(parametersString);
    }
    

    Once you have created signature base string then you the next step is to calculate signing key.

    • If you just need to make a signed request, then you create signing key based on your consumer key(shared secret) only. This the signing key to be used to make a signed request.
    • During authorization process, if you just made a request token request and recieved a temporary oauth token, then your singing key is based on your consumer key and that oauth token. This is the signing key used to make request to get the access token.
    • If a user authorized your application and you have the relevant access token, then your signing key would be your consumer key and access token. This is the signing key to make a protected request.

    This is the code that will generate the signing key:

    public static string GetSigningKey(string ConsumerSecret, string OAuthTokenSecret = null)
    {
        return ConsumerSecret + "&" + (OAuthTokenSecret != null ? OAuthTokenSecret : "");
    }
    

    In your case, to make a signed request, you just need pass null value for OAuthTokenSecret parameter.

    Ok, now you have a signature base string, all you need to do now is to sign using HMAC-SHA1 algorithm:

    public static string Sign(string signatureBaseString, string signingKey)
    {
        var keyBytes = System.Text.Encoding.ASCII.GetBytes(signingKey);
        using (var myhmacsha1 = new System.Security.Cryptography.HMACSHA1(keyBytes)) {
            byte[] byteArray = System.Text.Encoding.ASCII.GetBytes(signatureBaseString);
            var stream = new MemoryStream(byteArray);
            var signedValue = myhmacsha1.ComputeHash(stream);
            var result = Convert.ToBase64String(signedValue, Base64FormattingOptions.None);
            return result;
        }
    }
    

    To sum up this is the whole process for calculating signature:

        public virtual string GetSignature(string consumerSecret, string tokenSecret, string uri, string method, params ParameterSet[] parameters)
        {
            var allParameters = parameters.SelectMany(p => p.ToList()).ToArray();
            var parametersString = CalculateSignatureBaseString(allParameters);
            var signatureBaseString = OAuth1aUtil.CalcualteSignatureBaseString(method, uri, parametersString);
            var sigingKey = GetSigningKey(consumerSecret, tokenSecret);
            var signature = Sign(signatureBaseString, sigingKey);
            return signature;
        }
    

    Making request

    Now you just need to make a valid http request and add the oauth parameters to the request as the 'Authorization' header.