2017-07-26 83 views
0

我想通過oauth(.net core C#)訪問我的Withings/Nokia縮放數據。Withings API - 無效簽名

說明書,可以發現: https://oauth.withings.com/en/api/oauthguide

和API指南這裏: https://developer.health.nokia.com/api#step1

我已經實現了第1部分 - 我得到一個身份驗證令牌和祕密。

部分 - 手動我已經檢索授權我withings我的應用程序的使用代碼擴展的數據 - 即授權碼的回調(通過API開發者網頁)的結果。我假設這隻需要一次授權我的應用程序的訪問永久。如果我重新授權應用程序,我已將此值硬編碼到我的代碼中並進行更新。

現在我被困在第3部分 - 獲得訪問令牌/祕密。 錯誤=無效的簽名

(使用上面的頁面我已經能夠檢索我的4年價值數據,所以我知道它應該工作)。

我的基本簽名是相同於上述API測試頁(除了隨機數,簽名和時間戳)。

我的網址是相同於上述API測試頁(除了隨機數和時間戳)。

對我來說這個神祕的原因是爲什麼它適用於第1部分而不是第3部分。 它是不好的代碼,或者只是請求令牌必須在請求發出之前對應用程序/用戶數據進行授權? 但是當然,我不必每次都重新授權給用戶?

我本來搞砸第1部分,給了一個無效的簽名錯誤 - 這顯然與簽名的問題 - 但在第三部分我已經重新檢查了簽名,這是件好事。

private const string AUTH_VERSION = "1.0"; 
private const string SIGNATURE_METHOD = "HMAC-SHA1"; 

private const string BASE_URL_REQUEST_AUTH_TOKEN = "https://developer.health.nokia.com/account/request_token"; 
private const string BASE_URL_REQUEST_ACCESS_TOKEN = "https://developer.health.nokia.com/account/access_token"; 

...

Withings w = new Withings(); 
OAuthToken t = await w.GetOAuthToken(); 
string token = t.OAuth_Token; 
string secret = t.OAuth_Token_Secret; 
OAuthAccessToken at = await w.GetOAuthAccess(t); 
string aToken = at.OAuth_Token; 
string aTokenSecret = at.OAuth_Token_Secret; 

...

public async Task<OAuthAccessToken> GetOAuthAccess(OAuthToken authToken) 
     { 
      OAuthAccessToken token = new OAuthAccessToken(); 

      try 
      { 
       string random = GetRandomString(); 
       string timestamp = GetTimestamp(); 

       string baseSignature = GetOAuthAccessSignature(authToken, random, timestamp); 
       string hashSignature = ComputeHash(baseSignature, CONSUMER_SECRET, authToken.OAuth_Token_Secret); 
       string codeSignature = UrlEncode(hashSignature); 

       string requestUrl = GetOAuthAccessUrl(authToken, codeSignature, random, timestamp); 

       HttpResponseMessage response = await client.GetAsync(requestUrl); 
       string responseBodyAsText = await response.Content.ReadAsStringAsync(); 

       string[] parameters = responseBodyAsText.Split('&'); 
       token.OAuth_Token = parameters[0].Split('=')[1].ToString(); 
       token.OAuth_Token_Secret = parameters[1].Split('=')[1].ToString(); 

      } 
      catch (Exception ex) 
      { 

      } 
      return token; 
     } 

private string GetOAuthAccessSignature(OAuthToken authToken, string random, string timestamp) 
     { 
      var urlDict = new SortedDictionary<string, string> 
      { 
       //{ "oauth_consumer_key", CONSUMER_KEY}, 
       { "oauth_nonce", random}, 
       { "oauth_signature_method", UrlEncode(SIGNATURE_METHOD)}, 
       { "oauth_timestamp", timestamp}, 
       { "oauth_token", END_USER_AUTHORISATION_REQUEST_TOKEN }, 
       { "oauth_version", AUTH_VERSION} 
      }; 

      StringBuilder sb = new StringBuilder(); 
      sb.Append("GET&" + UrlEncode(BASE_URL_REQUEST_ACCESS_TOKEN) + "&oauth_consumer_key%3D" + CONSUMER_KEY); 

      int count = 0; 
      foreach (var urlItem in urlDict) 
      { 
       count++; 
       if (count >= 1) sb.Append(UrlEncode("&")); 
       sb.Append(UrlEncode(urlItem.Key + "=" + urlItem.Value)); 
      } 

      return sb.ToString(); 
     } 

     private string GetOAuthAccessUrl(OAuthToken authToken, string signature, string random, string timestamp) 
     { 
      var urlDict = new SortedDictionary<string, string> 
      { 
       { "oauth_consumer_key", CONSUMER_KEY}, 
       { "oauth_nonce", random}, 
       { "oauth_signature", signature }, 
       { "oauth_signature_method", UrlEncode(SIGNATURE_METHOD)}, 
       { "oauth_timestamp", timestamp}, 
       { "oauth_token", END_USER_AUTHORISATION_REQUEST_TOKEN }, 
       { "oauth_version", AUTH_VERSION} 
      }; 

      StringBuilder sb = new StringBuilder(); 
      sb.Append(BASE_URL_REQUEST_ACCESS_TOKEN + "?"); 

      int count = 0; 
      foreach (var urlItem in urlDict) 
      { 
       count++; 
       if (count > 1) sb.Append("&"); 
       sb.Append(urlItem.Key + "=" + urlItem.Value); 
      } 

      return sb.ToString(); 
     } 

注:

  • 我已下令我的參數
  • 我有一個體面的進行urlencode(正確我wr翁)
  • 我有一個HMAC-SHA1散列(指正錯誤)
  • 在使用開放圖書館不感興趣 - 我想解決這個代碼,而無需第三方工具

下面是輔助方法我使用:

private string ComputeHash(string data, string consumerSecret, string tokenSecret = null) 
     { 
      // Construct secret key based on consumer key (and optionally include token secret) 
      string secretKey = consumerSecret + "&"; 
      if (tokenSecret != null) secretKey += tokenSecret; 

      // Initialise with secret key 
      System.Security.Cryptography.HMACSHA1 hmacsha = new System.Security.Cryptography.HMACSHA1(Encoding.ASCII.GetBytes(secretKey)); 
      hmacsha.Initialize(); 

      // Convert data into byte array 
      byte[] dataBuffer = Encoding.ASCII.GetBytes(data); 

      // Computer hash of data byte array 
      byte[] hashBytes = hmacsha.ComputeHash(dataBuffer); 

      // Return the base 64 of the result 
      return Convert.ToBase64String(hashBytes); 
     } 

     // Get random string 
     private string GetRandomString() 
     { 
      return Guid.NewGuid().ToString().Replace("-", ""); 
     } 

     // Get timestamp 
     private string GetTimestamp() 
     { 
      var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); 
      return Convert.ToInt64(ts.TotalSeconds).ToString(); 
     } 

     // Url Encode (as Uri.Escape is reported to be not appropriate for this purpose) 
     protected string UnreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~"; 
     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(); 
     } 

謝謝,丹。

回答

1

解決 - 這是我如何OAuth的作品的理解問題。

步驟1 - 獲取令牌(這允許您根據您的API賬戶申請提出要求)

步驟2 - 創建用戶重定向的URL(使用起的2分鐘令牌上面)授權您的Withings api應用程序使用特定用戶的帳戶。返回同樣的道理,你通過它 - 但現在將被允許發出請求在步驟3

步驟3 - 請訪問令牌 - 這會給你一個令牌和祕密字符串,它允許你繼續訪問此用戶的帳戶(用於您的api帳戶應用程序)。

第4步 - 請求數據 - 與前面所有步驟類似的方法 - 很容易。返回一大串很長的數據。閱讀API文件,就像你可以過濾 - 這是我將要做的事情,因爲我有大約4/5年的「有趣」重量數據。

我在做第1步,然後進行第3步,認爲從第2步返回的代碼(沒有注意到它與放入的代碼相同)可以存儲並用於第3步,而無需重新授權。

實際上(以及我所做的)是按照API演示接口在步驟3中生成身份驗證令牌和密碼,這就是您繼續請求數據所需的全部內容。您只需要一次用戶授權,並將用戶帳戶/某種商店的第3步授權令牌/祕密存儲起來。

另請注意,您可以調用通知 - 新權重會觸發您的網站自動刷新數據。太好了,如果你只是想登錄並看到最新的數據,而沒有手動觸發刷新數據/導致進一步的延遲。

請注意,該API有過濾選項 - 所以請確保您閱讀這些選項。

這裏的一些(基本)代碼可能會出現一些使用:

 public async Task<string> GetData_BodyMeasures() 
    { 
     string rawdata = ""; 

     try 
     { 
      string random = GetRandomString(); 
      string timestamp = GetTimestamp(); 

      string baseSignature = GetDataSignature_BodyMeasure(random, timestamp); 
      string hashSignature = ComputeHash(baseSignature, CONSUMER_SECRET, ACCESS_OAUTH_TOKEN_SECRET); 
      string codeSignature = UrlEncode(hashSignature); 

      string requestUrl = GetData_BodyMeasure_Url(codeSignature, random, timestamp); 

      HttpResponseMessage response = await client.GetAsync(requestUrl); 
      string responseBodyAsText = await response.Content.ReadAsStringAsync(); 

      rawdata = responseBodyAsText; 

     } 
     catch (Exception ex) 
     { 

     } 
     return rawdata; 
    } 




    private string GetDataSignature_BodyMeasure(string random, string timestamp) 
    { 
     var urlDict = new SortedDictionary<string, string> 
     { 
      { "oauth_consumer_key", CONSUMER_KEY}, 
      { "oauth_nonce", random}, 
      { "oauth_signature_method", SIGNATURE_METHOD}, 
      { "oauth_timestamp", timestamp}, 
      { "oauth_token", ACCESS_OAUTH_TOKEN }, 
      { "oauth_version", AUTH_VERSION}, 
      { "userid", USER_ID } 
     }; 

     StringBuilder sb = new StringBuilder(); 
     sb.Append("GET&" + UrlEncode(BASE_URL_REQUEST_BODY_MEASURE) + "&action%3Dgetmeas"); 

     int count = 0; 
     foreach (var urlItem in urlDict) 
     { 
      count++; 
      if (count >= 1) sb.Append(UrlEncode("&")); 
      sb.Append(UrlEncode(urlItem.Key + "=" + urlItem.Value)); 
     } 

     return sb.ToString(); 
    } 


    private string GetData_BodyMeasure_Url(string signature, string random, string timestamp) 
    { 
     var urlDict = new SortedDictionary<string, string> 
     { 
      { "action", "getmeas"}, 
      { "oauth_consumer_key", CONSUMER_KEY}, 
      { "oauth_nonce", random}, 
      { "oauth_signature", signature }, 
      { "oauth_signature_method", UrlEncode(SIGNATURE_METHOD)}, 
      { "oauth_timestamp", timestamp}, 
      { "oauth_token", ACCESS_OAUTH_TOKEN }, 
      { "oauth_version", AUTH_VERSION}, 
      { "userid", USER_ID } 
     }; 

     StringBuilder sb = new StringBuilder(); 
     sb.Append(BASE_URL_REQUEST_BODY_MEASURE + "?"); 

     int count = 0; 
     foreach (var urlItem in urlDict) 
     { 
      count++; 
      if (count >= 1) sb.Append("&"); 
      sb.Append(urlItem.Key + "=" + urlItem.Value); 
     } 

     return sb.ToString(); 
    }