diff --git a/Withings.NET/Client/OAuthBase.cs b/Withings.NET/Client/OAuthBase.cs index 2e2028e..a694d05 100644 --- a/Withings.NET/Client/OAuthBase.cs +++ b/Withings.NET/Client/OAuthBase.cs @@ -1,292 +1,299 @@ -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Text; -using RestSharp.Extensions.MonoHttp; -using static System.String; - -namespace Withings.NET.Client -{ - public class OAuthBase - { - - /// - /// Provides a predefined set of algorithms that are supported officially by the protocol - /// - public enum SignatureTypes - { - HMACSHA1, - PLAINTEXT, - RSASHA1 - } - - /// - /// Provides an internal structure to sort the query parameter - /// - protected class QueryParameter - { - public QueryParameter(string name, string value) - { - Name = name; - Value = value; - } - - public string Name { get; } - - public string Value { get; } - } - - /// - /// Comparer class used to perform the sorting of the query parameters - /// - protected class QueryParameterComparer : IComparer - { - - #region IComparer Members - - public int Compare(QueryParameter x, QueryParameter y) => x?.Name == y?.Name ? CompareOrdinal(x?.Value, y?.Value) : CompareOrdinal(x?.Name, y.Name); - - #endregion - } - - protected const string OAuthVersion = "1.0"; - protected const string OAuthParameterPrefix = "oauth_"; - protected const string OAuthConsumerKeyKey = "oauth_consumer_key"; - protected const string OAuthCallbackKey = "oauth_callback"; - protected const string OAuthVersionKey = "oauth_version"; - protected const string OAuthSignatureMethodKey = "oauth_signature_method"; - protected const string OAuthSignatureKey = "oauth_signature"; - protected const string OAuthTimestampKey = "oauth_timestamp"; - protected const string OAuthNonceKey = "oauth_nonce"; - protected const string OAuthTokenKey = "oauth_token"; - protected const string OAuthTokenSecretKey = "oauth_token_secret"; - protected const string Hmacsha1SignatureType = "HMAC-SHA1"; - protected const string PlainTextSignatureType = "PLAINTEXT"; - protected const string Rsasha1SignatureType = "RSA-SHA1"; - - protected Random Random = new Random(); - - protected string UnreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; - - /// - /// Helper function to compute a hash value - /// - /// The hashing algoirhtm used. If that algorithm needs some initialization, like HMAC and its derivatives, they should be initialized prior to passing it to this function - /// The data to hash - /// a Base64 string of the hash value - private string ComputeHash(HashAlgorithm hashAlgorithm, string data) - { - if (hashAlgorithm == null) - throw new ArgumentNullException(nameof(hashAlgorithm)); - - if (IsNullOrEmpty(data)) - throw new ArgumentNullException(nameof(data)); - - byte[] dataBuffer = Encoding.ASCII.GetBytes(data); - byte[] hashBytes = hashAlgorithm.ComputeHash(dataBuffer); - - return Convert.ToBase64String(hashBytes); - } - - /// - /// Internal function to cut out all non oauth query string parameters (all parameters not begining with "oauth_") - /// - /// The query string part of the Url - /// A list of QueryParameter each containing the parameter name and value - private List GetQueryParameters(string parameters) - { - if (parameters.StartsWith("?")) - parameters = parameters.Remove(0, 1); - var result = new List(); - if (!IsNullOrEmpty(parameters)) - { - string[] p = parameters.Split('&'); - foreach (var s in p) - { - if (!IsNullOrEmpty(s) && !s.StartsWith(OAuthParameterPrefix)) - { - if (s.IndexOf('=') > -1) - { - string[] temp = s.Split('='); - result.Add(new QueryParameter(temp[0], temp[1])); - } - else - { - result.Add(new QueryParameter(s, Empty)); - } - } - } - } - return result; - } - - /// - /// This is a different Url Encode implementation since the default .NET one outputs the percent encoding in lower case. - /// While this is not a problem with the percent encoding spec, it is used in upper case throughout OAuth - /// - /// The value to Url encode - /// Returns a Url encoded string - protected string UrlEncode(string value) - { - var result = new StringBuilder(); - foreach (var symbol in value) - { - if (UnreservedChars.IndexOf(symbol) != -1) - result.Append(symbol); - else - result.Append('%' + $"{(int) symbol:X2}"); - } - return result.ToString(); - } - - /// - /// Normalizes the request parameters according to the spec - /// - /// The list of parameters already sorted - /// a string representing the normalized parameters - protected string NormalizeRequestParameters(IList parameters) - { - var sb = new StringBuilder(); - for (var i = 0; i < parameters.Count; i++) - { - var p = parameters[i]; - sb.AppendFormat("{0}={1}", p.Name, p.Value); - - if (i < parameters.Count - 1) - sb.Append("&"); - } - return sb.ToString(); - } - - /// - /// Generate the signature base that is used to produce the signature - /// - /// The full url that needs to be signed including its non OAuth url parameters - /// The consumer key - /// The token, if available. If not available pass null or an empty string - /// The token secret, if available. If not available pass null or an empty string - /// The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc) - /// - /// The signature type. To use the default values use OAuthBase.SignatureTypes. - /// - /// - /// - /// The signature base - public string GenerateSignatureBase(Uri url, string consumerKey, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, string signatureType, out string normalizedUrl, out string normalizedRequestParameters) - { - if (token == null) - token = Empty; - if (IsNullOrEmpty(consumerKey)) - throw new ArgumentNullException(nameof(consumerKey)); - if (IsNullOrEmpty(httpMethod)) - throw new ArgumentNullException(nameof(httpMethod)); - if (IsNullOrEmpty(signatureType)) - throw new ArgumentNullException(nameof(signatureType)); - - List parameters = GetQueryParameters(url.Query); - parameters.Add(new QueryParameter(OAuthVersionKey, OAuthVersion)); - parameters.Add(new QueryParameter(OAuthNonceKey, nonce)); - parameters.Add(new QueryParameter(OAuthTimestampKey, timeStamp)); - parameters.Add(new QueryParameter(OAuthSignatureMethodKey, signatureType)); - parameters.Add(new QueryParameter(OAuthConsumerKeyKey, consumerKey)); - - if (!IsNullOrEmpty(token)) - parameters.Add(new QueryParameter(OAuthTokenKey, token)); - - parameters.Sort(new QueryParameterComparer()); - - normalizedUrl = $"{url.Scheme}://{url.Host}"; - if (!(url.Scheme == "http" && url.Port == 80 || url.Scheme == "https" && url.Port == 443)) - normalizedUrl += ":" + url.Port; - normalizedUrl += url.AbsolutePath; - normalizedRequestParameters = NormalizeRequestParameters(parameters); - - var signatureBase = new StringBuilder(); - signatureBase.AppendFormat("{0}&", httpMethod.ToUpper()); - signatureBase.AppendFormat("{0}&", UrlEncode(normalizedUrl)); - signatureBase.AppendFormat("{0}", UrlEncode(normalizedRequestParameters)); - return signatureBase.ToString(); - } - - /// - /// Generate the signature value based on the given signature base and hash algorithm - /// - /// The signature based as produced by the GenerateSignatureBase method or by any other means - /// The hash algorithm used to perform the hashing. If the hashing algorithm requires initialization or a key it should be set prior to calling this method - /// A base64 string of the hash value - public string GenerateSignatureUsingHash(string signatureBase, HashAlgorithm hash) => ComputeHash(hash, signatureBase); - - /// - /// Generates a signature using the HMAC-SHA1 algorithm - /// - /// The full url that needs to be signed including its non OAuth url parameters - /// The consumer key - /// The consumer seceret - /// The token, if available. If not available pass null or an empty string - /// The token secret, if available. If not available pass null or an empty string - /// The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc) - /// - /// - /// - /// - /// A base64 string of the hash value - public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, out string normalizedUrl, out string normalizedRequestParameters) => GenerateSignature(url, consumerKey, consumerSecret, token, tokenSecret, httpMethod, timeStamp, nonce, SignatureTypes.HMACSHA1, out normalizedUrl, out normalizedRequestParameters); - - /// - /// Generates a signature using the specified signatureType - /// - /// The full url that needs to be signed including its non OAuth url parameters - /// The consumer key - /// The consumer seceret - /// The token, if available. If not available pass null or an empty string - /// The token secret, if available. If not available pass null or an empty string - /// The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc) - /// - /// The type of signature to use - /// - /// - /// - /// A base64 string of the hash value - public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, SignatureTypes signatureType, out string normalizedUrl, out string normalizedRequestParameters) - { - normalizedUrl = null; - normalizedRequestParameters = null; - switch (signatureType) - { - case SignatureTypes.PLAINTEXT: - return HttpUtility.UrlEncode($"{consumerSecret}&{tokenSecret}"); - case SignatureTypes.HMACSHA1: - var signatureBase = GenerateSignatureBase(url, consumerKey, token, tokenSecret, httpMethod, timeStamp, nonce, Hmacsha1SignatureType, out normalizedUrl, out normalizedRequestParameters); - - var hmacsha1 = new HMACSHA1 - { - Key = Encoding.ASCII.GetBytes( - $"{UrlEncode(consumerSecret)}&{(IsNullOrEmpty(tokenSecret) ? "" : UrlEncode(tokenSecret))}") - }; - - return GenerateSignatureUsingHash(signatureBase, hmacsha1); - case SignatureTypes.RSASHA1: - throw new NotImplementedException(); - default: - throw new ArgumentException("Unknown signature type", nameof(signatureType)); - } - } - - /// - /// Generate the timestamp for the signature - /// - /// - public virtual string GenerateTimeStamp() - { - // Default implementation of UNIX time of the current UTC time - var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); - return Convert.ToInt64(ts.TotalSeconds).ToString(); - } - - /// - /// Generate a nonce - /// - /// - public virtual string GenerateNonce() => Random.Next(123400, 9999999).ToString(); - } +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Text; +using static System.String; + +namespace Withings.NET.Client +{ + public class OAuthBase + { + + /// + /// Provides a predefined set of algorithms that are supported officially by the protocol + /// + public enum SignatureTypes + { + HMACSHA1, + PLAINTEXT, + RSASHA1 + } + + /// + /// Provides an internal structure to sort the query parameter + /// + protected class QueryParameter + { + public QueryParameter(string name, string value) + { + Name = name; + Value = value; + } + + public string Name { get; } + + public string Value { get; } + } + + /// + /// Comparer class used to perform the sorting of the query parameters + /// + protected class QueryParameterComparer : IComparer + { + + #region IComparer Members + + public int Compare(QueryParameter x, QueryParameter y) => x?.Name == y?.Name ? CompareOrdinal(x?.Value, y?.Value) : CompareOrdinal(x?.Name, y.Name); + + #endregion + } + + protected const string OAuthVersion = "1.0"; + protected const string OAuthParameterPrefix = "oauth_"; + protected const string OAuthConsumerKeyKey = "oauth_consumer_key"; + protected const string OAuthCallbackKey = "oauth_callback"; + protected const string OAuthVersionKey = "oauth_version"; + protected const string OAuthSignatureMethodKey = "oauth_signature_method"; + protected const string OAuthSignatureKey = "oauth_signature"; + protected const string OAuthTimestampKey = "oauth_timestamp"; + protected const string OAuthNonceKey = "oauth_nonce"; + protected const string OAuthTokenKey = "oauth_token"; + protected const string OAuthTokenSecretKey = "oauth_token_secret"; + protected const string Hmacsha1SignatureType = "HMAC-SHA1"; + protected const string PlainTextSignatureType = "PLAINTEXT"; + protected const string Rsasha1SignatureType = "RSA-SHA1"; + + protected static Random Random = new Random(); + private static readonly object _randomLock = new object(); + + protected string UnreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; + + /// + /// Helper function to compute a hash value + /// + /// The hashing algoirhtm used. If that algorithm needs some initialization, like HMAC and its derivatives, they should be initialized prior to passing it to this function + /// The data to hash + /// a Base64 string of the hash value + private string ComputeHash(HashAlgorithm hashAlgorithm, string data) + { + if (hashAlgorithm == null) + throw new ArgumentNullException(nameof(hashAlgorithm)); + + if (IsNullOrEmpty(data)) + throw new ArgumentNullException(nameof(data)); + + byte[] dataBuffer = Encoding.ASCII.GetBytes(data); + byte[] hashBytes = hashAlgorithm.ComputeHash(dataBuffer); + + return Convert.ToBase64String(hashBytes); + } + + /// + /// Internal function to cut out all non oauth query string parameters (all parameters not begining with "oauth_") + /// + /// The query string part of the Url + /// A list of QueryParameter each containing the parameter name and value + private List GetQueryParameters(string parameters) + { + if (parameters.StartsWith("?")) + parameters = parameters.Remove(0, 1); + var result = new List(); + if (!IsNullOrEmpty(parameters)) + { + string[] p = parameters.Split('&'); + foreach (var s in p) + { + if (!IsNullOrEmpty(s) && !s.StartsWith(OAuthParameterPrefix)) + { + if (s.IndexOf('=') > -1) + { + string[] temp = s.Split('='); + result.Add(new QueryParameter(temp[0], temp[1])); + } + else + { + result.Add(new QueryParameter(s, Empty)); + } + } + } + } + return result; + } + + /// + /// This is a different Url Encode implementation since the default .NET one outputs the percent encoding in lower case. + /// While this is not a problem with the percent encoding spec, it is used in upper case throughout OAuth + /// + /// The value to Url encode + /// Returns a Url encoded string + protected string UrlEncode(string value) + { + var result = new StringBuilder(); + foreach (var symbol in value) + { + if (UnreservedChars.IndexOf(symbol) != -1) + result.Append(symbol); + else + result.Append('%' + $"{(int) symbol:X2}"); + } + return result.ToString(); + } + + /// + /// Normalizes the request parameters according to the spec + /// + /// The list of parameters already sorted + /// a string representing the normalized parameters + protected string NormalizeRequestParameters(IList parameters) + { + var sb = new StringBuilder(); + for (var i = 0; i < parameters.Count; i++) + { + var p = parameters[i]; + sb.AppendFormat("{0}={1}", p.Name, p.Value); + + if (i < parameters.Count - 1) + sb.Append("&"); + } + return sb.ToString(); + } + + /// + /// Generate the signature base that is used to produce the signature + /// + /// The full url that needs to be signed including its non OAuth url parameters + /// The consumer key + /// The token, if available. If not available pass null or an empty string + /// The token secret, if available. If not available pass null or an empty string + /// The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc) + /// + /// The signature type. To use the default values use OAuthBase.SignatureTypes. + /// + /// + /// + /// The signature base + public string GenerateSignatureBase(Uri url, string consumerKey, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, string signatureType, out string normalizedUrl, out string normalizedRequestParameters) + { + if (token == null) + token = Empty; + if (IsNullOrEmpty(consumerKey)) + throw new ArgumentNullException(nameof(consumerKey)); + if (IsNullOrEmpty(httpMethod)) + throw new ArgumentNullException(nameof(httpMethod)); + if (IsNullOrEmpty(signatureType)) + throw new ArgumentNullException(nameof(signatureType)); + + List parameters = GetQueryParameters(url.Query); + parameters.Add(new QueryParameter(OAuthVersionKey, OAuthVersion)); + parameters.Add(new QueryParameter(OAuthNonceKey, nonce)); + parameters.Add(new QueryParameter(OAuthTimestampKey, timeStamp)); + parameters.Add(new QueryParameter(OAuthSignatureMethodKey, signatureType)); + parameters.Add(new QueryParameter(OAuthConsumerKeyKey, consumerKey)); + + if (!IsNullOrEmpty(token)) + parameters.Add(new QueryParameter(OAuthTokenKey, token)); + + parameters.Sort(new QueryParameterComparer()); + + normalizedUrl = $"{url.Scheme}://{url.Host}"; + if (!(url.Scheme == "http" && url.Port == 80 || url.Scheme == "https" && url.Port == 443)) + normalizedUrl += ":" + url.Port; + normalizedUrl += url.AbsolutePath; + normalizedRequestParameters = NormalizeRequestParameters(parameters); + + var signatureBase = new StringBuilder(); + signatureBase.AppendFormat("{0}&", httpMethod.ToUpper()); + signatureBase.AppendFormat("{0}&", UrlEncode(normalizedUrl)); + signatureBase.AppendFormat("{0}", UrlEncode(normalizedRequestParameters)); + return signatureBase.ToString(); + } + + /// + /// Generate the signature value based on the given signature base and hash algorithm + /// + /// The signature based as produced by the GenerateSignatureBase method or by any other means + /// The hash algorithm used to perform the hashing. If the hashing algorithm requires initialization or a key it should be set prior to calling this method + /// A base64 string of the hash value + public string GenerateSignatureUsingHash(string signatureBase, HashAlgorithm hash) => ComputeHash(hash, signatureBase); + + /// + /// Generates a signature using the HMAC-SHA1 algorithm + /// + /// The full url that needs to be signed including its non OAuth url parameters + /// The consumer key + /// The consumer seceret + /// The token, if available. If not available pass null or an empty string + /// The token secret, if available. If not available pass null or an empty string + /// The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc) + /// + /// + /// + /// + /// A base64 string of the hash value + public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, out string normalizedUrl, out string normalizedRequestParameters) => GenerateSignature(url, consumerKey, consumerSecret, token, tokenSecret, httpMethod, timeStamp, nonce, SignatureTypes.HMACSHA1, out normalizedUrl, out normalizedRequestParameters); + + /// + /// Generates a signature using the specified signatureType + /// + /// The full url that needs to be signed including its non OAuth url parameters + /// The consumer key + /// The consumer seceret + /// The token, if available. If not available pass null or an empty string + /// The token secret, if available. If not available pass null or an empty string + /// The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc) + /// + /// The type of signature to use + /// + /// + /// + /// A base64 string of the hash value + public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, SignatureTypes signatureType, out string normalizedUrl, out string normalizedRequestParameters) + { + normalizedUrl = null; + normalizedRequestParameters = null; + switch (signatureType) + { + case SignatureTypes.PLAINTEXT: + return System.Net.WebUtility.UrlEncode($"{consumerSecret}&{tokenSecret}"); + case SignatureTypes.HMACSHA1: + var signatureBase = GenerateSignatureBase(url, consumerKey, token, tokenSecret, httpMethod, timeStamp, nonce, Hmacsha1SignatureType, out normalizedUrl, out normalizedRequestParameters); + + using (var hmacsha1 = new HMACSHA1 + { + Key = Encoding.ASCII.GetBytes( + $"{UrlEncode(consumerSecret)}&{(IsNullOrEmpty(tokenSecret) ? "" : UrlEncode(tokenSecret))}") + }) + { + return GenerateSignatureUsingHash(signatureBase, hmacsha1); + } + case SignatureTypes.RSASHA1: + throw new NotImplementedException(); + default: + throw new ArgumentException("Unknown signature type", nameof(signatureType)); + } + } + + /// + /// Generate the timestamp for the signature + /// + /// + public virtual string GenerateTimeStamp() + { + // Default implementation of UNIX time of the current UTC time + var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); + return Convert.ToInt64(ts.TotalSeconds).ToString(); + } + + /// + /// Generate a nonce + /// + /// + public virtual string GenerateNonce() + { + lock (_randomLock) + { + return Random.Next(123400, 9999999).ToString(); + } + } + } } \ No newline at end of file diff --git a/Withings.NET/Client/WithingsClient.cs b/Withings.NET/Client/WithingsClient.cs index 1b2be8c..28b2f43 100644 --- a/Withings.NET/Client/WithingsClient.cs +++ b/Withings.NET/Client/WithingsClient.cs @@ -10,6 +10,7 @@ namespace Withings.NET.Client { public class WithingsClient { + private static readonly OAuthBase _oAuth = new OAuthBase(); readonly WithingsCredentials _credentials; const string BaseUri = "https://wbsapi.withings.net/v2"; @@ -27,7 +28,7 @@ public async Task GetActivityMeasures(DateTime startDay, DateTime .SetQueryParam("userid", userId) .SetQueryParam("startdateymd", $"{startDay:yyyy-MM-dd}") .SetQueryParam("enddateymd", $"{endDay:yyyy-MM-dd}"); - var oAuth = new OAuthBase(); + var oAuth = _oAuth; string nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString())); string timeStamp = oAuth.GenerateTimeStamp(); string normalizedUrl; @@ -51,7 +52,7 @@ public async Task GetActivityMeasures(DateTime lastUpdate, string .SetQueryParam("action", "getactivity") .SetQueryParam("userid", userId) .SetQueryParam("date", $"{lastUpdate:yyyy-MM-dd}"); - var oAuth = new OAuthBase(); + var oAuth = _oAuth; string nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString())); string timeStamp = oAuth.GenerateTimeStamp(); string normalizedUrl; @@ -80,7 +81,7 @@ public async Task GetSleepSummary(string startday, string endday, .SetQueryParam("action", "getsummary") .SetQueryParam("startdateymd", startday) .SetQueryParam("enddateymd", endday); - var oAuth = new OAuthBase(); + var oAuth = _oAuth; string nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString())); string timeStamp = oAuth.GenerateTimeStamp(); string normalizedUrl; @@ -105,7 +106,7 @@ public async Task GetSleepMeasures(string userid, DateTime startd .SetQueryParam("action", "get") .SetQueryParam("startdate", startday.ToUnixTime()) .SetQueryParam("enddate", endday.ToUnixTime()); - var oAuth = new OAuthBase(); + var oAuth = _oAuth; string nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString())); string timeStamp = oAuth.GenerateTimeStamp(); string normalizedUrl; @@ -131,7 +132,7 @@ public async Task GetWorkouts(string startday, string endday, str { var query = BaseUri.AppendPathSegment("measure").SetQueryParam("action", "getworkouts") .SetQueryParam("startdateymd", startday).SetQueryParam("enddateymd", endday); - var oAuth = new OAuthBase(); + var oAuth = _oAuth; var nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString())); var timeStamp = oAuth.GenerateTimeStamp(); string normalizedUrl; @@ -161,7 +162,7 @@ public async Task GetIntraDayActivity(string userId, DateTime sta .SetQueryParam("userid", userId) .SetQueryParam("startdate", start.ToUnixTime()) .SetQueryParam("enddate", end.ToUnixTime()); - var oAuth = new OAuthBase(); + var oAuth = _oAuth; var nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString())); var timeStamp = oAuth.GenerateTimeStamp(); string normalizedUrl; @@ -191,7 +192,7 @@ public async Task GetBodyMeasures(string userid, DateTime start, .SetQueryParam("userid", userid) .SetQueryParam("startdate", start.ToUnixTime()) .SetQueryParam("enddate", end.ToUnixTime()); - var oAuth = new OAuthBase(); + var oAuth = _oAuth; var nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString())); var timeStamp = oAuth.GenerateTimeStamp(); string normalizedUrl; @@ -216,7 +217,7 @@ public async Task GetBodyMeasures(string userid, DateTime lastupd .SetQueryParam("action", "getmeas") .SetQueryParam("userid", userid) .SetQueryParam("lastupdate", lastupdate.ToUnixTime()); - var oAuth = new OAuthBase(); + var oAuth = _oAuth; var nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString())); var timeStamp = oAuth.GenerateTimeStamp(); string normalizedUrl;