OAuth 1 - 401 Unautorisiert

  • C#

Es gibt 4 Antworten in diesem Thema. Der letzte Beitrag () ist von Gather.

    OAuth 1 - 401 Unautorisiert

    Hallo,

    ich schreibe zurzeit für @Gather das ID3-Tag-Programm, das die Beatport-API benutzt, die noch auf OAuth 1.0a basiert.
    Habe dann nach einer API gegooglet und dann das gefunden: cropperplugins.codeplex.com/So…ngeset/view/65377#1710422
    Es funktionierte auch gut, habe das dann so angewendet:

    C#-Quellcode

    1. OAuth.Manager oAuth = new Manager
    2. {
    3. ["consumer_key"] = "****",
    4. ["consumer_secret"] = "****"
    5. };
    6. // Hier wird der Request-Token geholt und dann die URL geöffnet, bei dem dann der Login erscheint:
    7. OAuthResponse res = oAuth.AcquireRequestToken("https://oauth-api.beatport.com/identity/1/oauth/request-token", "POST");
    8. Process.Start("https://oauth-api.beatport.com/identity/1/oauth/authorize?oauth_token=" + res["oauth_token"]);
    9. // Zum Schluss wird dann so etwas angezeigt: oauth_token=****&oauth_verifier=****&oauth_callback_confirmed=true (das trage ich in eine Textbox ein), bei dem ich den Verifier übergebe, damit der den Access-Token holen kann.
    10. BeatportAPI.OAuthResponse res = new BeatportAPI.OAuthResponse(tbResponse.Text);
    11. oAuth.AcquireAccessToken("https://oauth-api.beatport.com/identity/1/oauth/access-token", "POST", res["oauth_verifier"]);


    Bis dahin hat alles gut funktioniert, es traten keine Fehler auf und der Access-Token wurde angezeigt.
    Doch als ich einen HTTP-Request machen will, wollte ich mir den Authorization-Header holen und diesen setzen.
    Da kam dann ein 401 - Unautorisiert.
    Aber da ich mich in dieser API nicht so richtig zurechtfinde, habe ich mir das speziell auf Beatport zugeschnitten und neu gemacht:
    Spoiler anzeigen

    C#-Quellcode

    1. public class BeatportAPI
    2. {
    3. private string consumerKey, consumerSecret, token, tokenSecret, signatureMethod = "HMAC-SHA1", version = "1.0";
    4. private WebClient webClient = new WebClient();
    5. private Random rand = new Random();
    6. public BeatportAPI(string consumerKey, string consumerSecret)
    7. {
    8. this.consumerKey = consumerKey;
    9. this.consumerSecret = consumerSecret;
    10. }
    11. public string AcquireRequestToken()
    12. {
    13. SortedDictionary<string, string> data = new SortedDictionary<string, string>()
    14. {
    15. { "oauth_consumer_key", this.consumerKey },
    16. { "oauth_nonce", this.GetNonce() },
    17. { "oauth_signature_method", this.signatureMethod },
    18. { "oauth_timestamp", this.GetTimeStamp().ToString() },
    19. { "oauth_version", this.version },
    20. { "oauth_callback", "oob" }
    21. };
    22. string signature = this.GetSignature("POST", "https://oauth-api.beatport.com/identity/1/oauth/request-token", data);
    23. data.Add("oauth_signature", signature);
    24. OAuthResponse res = this.Request("https://oauth-api.beatport.com/identity/1/oauth/request-token", "POST", data);
    25. this.token = res["oauth_token"];
    26. return "https://oauth-api.beatport.com/identity/1/oauth/authorize?oauth_token=" + this.token;
    27. }
    28. public void AcquireAccessToken(OAuthResponse data)
    29. {
    30. SortedDictionary<string, string> oAuthData = new SortedDictionary<string, string>()
    31. {
    32. { "oauth_consumer_key", this.consumerKey },
    33. { "oauth_nonce", this.GetNonce() },
    34. { "oauth_signature_method", this.signatureMethod },
    35. { "oauth_timestamp", this.GetTimeStamp().ToString() },
    36. { "oauth_version", this.version },
    37. { "oauth_callback", "oob" },
    38. { "oauth_verifier", data["oauth_verifier"] },
    39. { "oauth_token", this.token }
    40. };
    41. string signature = this.GetSignature("POST", "https://oauth-api.beatport.com/identity/1/oauth/access-token", oAuthData);
    42. oAuthData.Add("oauth_signature", signature);
    43. OAuthResponse res = this.Request("https://oauth-api.beatport.com/identity/1/oauth/access-token", "POST", oAuthData);
    44. this.token = res["oauth_token"];
    45. this.tokenSecret = res["oauth_token_secret"];
    46. }
    47. private string GetNonce()
    48. {
    49. char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
    50. string nonce = "";
    51. for (int i = 0; i < 8; i++)
    52. {
    53. nonce += chars[rand.Next(chars.Length)];
    54. }
    55. return nonce;
    56. }
    57. private long GetTimeStamp()
    58. {
    59. var timeSpan = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0));
    60. return (long)timeSpan.TotalSeconds;
    61. }
    62. private string EncodeRequestParameters(SortedDictionary<string, string> data, bool comma)
    63. {
    64. StringBuilder builder = new StringBuilder();
    65. if (comma)
    66. {
    67. foreach (var pair in data)
    68. {
    69. builder.Append(pair.Key + "=\"" + UrlEncode(pair.Value) + "\", ");
    70. }
    71. return builder.ToString().TrimEnd().TrimEnd(',');
    72. }
    73. else
    74. {
    75. foreach (var pair in data)
    76. {
    77. builder.Append(UrlEncode(pair.Key + "=" + pair.Value + "&"));
    78. }
    79. string str = builder.ToString();
    80. str = str.Substring(0, str.Length - 3);
    81. return str;
    82. }
    83. }
    84. private string GetSignature(string method, string url, SortedDictionary<string, string> data)
    85. {
    86. string dataStr = method + "&" + UrlEncode(url) + "&" + this.EncodeRequestParameters(data, false);
    87. string hashed = this.Hash(this.consumerSecret + "&" + this.tokenSecret, dataStr);
    88. return hashed;
    89. }
    90. private string UrlEncode(string value)
    91. {
    92. var result = new System.Text.StringBuilder();
    93. foreach (char symbol in value)
    94. {
    95. if (unreservedChars.IndexOf(symbol) != -1)
    96. result.Append(symbol);
    97. else
    98. result.Append('%' + String.Format("{0:X2}", (int)symbol));
    99. }
    100. return result.ToString();
    101. }
    102. private static string unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
    103. private string Hash(string key, string input)
    104. {
    105. HMACSHA1 hmacsha1 = new HMACSHA1(Encoding.ASCII.GetBytes(key));
    106. byte[] byteArray = Encoding.ASCII.GetBytes(input);
    107. MemoryStream stream = new MemoryStream(byteArray);
    108. byte[] hashValue = hmacsha1.ComputeHash(stream);
    109. return Convert.ToBase64String(hashValue);
    110. }
    111. private OAuthResponse Request(string url, string method, SortedDictionary<string, string> data)
    112. {
    113. string authHeader = "OAuth " + this.EncodeRequestParameters(data, true);
    114. var request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
    115. request.Headers.Add("Authorization", authHeader);
    116. request.Method = method;
    117. using (var response = (System.Net.HttpWebResponse)request.GetResponse())
    118. {
    119. using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
    120. {
    121. return new OAuthResponse(reader.ReadToEnd());
    122. }
    123. }
    124. }
    125. public string HttpRequest(string url, string method, NameValueCollection values)
    126. {
    127. SortedDictionary<string, string> oAuthData = new SortedDictionary<string, string>()
    128. {
    129. { "oauth_consumer_key", this.consumerKey },
    130. { "oauth_nonce", this.GetNonce() },
    131. { "oauth_signature_method", this.signatureMethod },
    132. { "oauth_timestamp", this.GetTimeStamp().ToString() },
    133. { "oauth_version", this.version },
    134. { "oauth_callback", "oob" },
    135. { "oauth_token", this.token }
    136. };
    137. foreach (var key in values.AllKeys)
    138. {
    139. oAuthData.Add(key, values[key]);
    140. }
    141. string signature = this.GetSignature(url, method, oAuthData);
    142. oAuthData.Add("oauth_signature", signature);
    143. string authHeader = "OAuth " + this.EncodeRequestParameters(oAuthData, true);
    144. var request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
    145. request.Headers.Add("Authorization", authHeader);
    146. request.Method = method;
    147. using (var response = (System.Net.HttpWebResponse)request.GetResponse())
    148. {
    149. using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
    150. {
    151. return reader.ReadToEnd();
    152. }
    153. }
    154. }
    155. public class OAuthResponse
    156. {
    157. private readonly Dictionary<string, string> values = new Dictionary<string, string>();
    158. public OAuthResponse(string text)
    159. {
    160. if (string.IsNullOrEmpty(text)) return;
    161. string[] v = text.Split('&');
    162. foreach (string p in v)
    163. {
    164. string[] s = p.Split('=');
    165. values.Add(s[0], s[1]);
    166. }
    167. }
    168. public string this[string key] => values[key];
    169. }
    170. }


    Diese wendet man so an:

    C#-Quellcode

    1. ​BeatportAPI api = new BeatportAPI("****", "****");
    2. // URL holen
    3. string url = api.AcquireRequestToken();
    4. Process.Start(url);
    5. // Access-Token holen
    6. BeatportAPI.OAuthResponse res = new BeatportAPI.OAuthResponse(tbResponse.Text);
    7. api.AcquireAccessToken(res);


    Bei diesem Code kommt in Zeile 138 dann auch ein 401-Error.
    Ich habe dann den Authorization-Header miteinander verglichen, die miteinander identisch waren:

    Quellcode

    1. ​// 1. API
    2. OAuth oauth_callback="oob", oauth_consumer_key="****", oauth_nonce="OfiiaUye", oauth_signature="0PG4NW0pRKoBwqUqH46I3axFIxc%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1468098188", oauth_token="****", oauth_verifier="****", oauth_version="1.0"
    3. // Meine API
    4. OAuth oauth_callback="oob", oauth_consumer_key="****", oauth_nonce="OfiiaUye", oauth_signature="0PG4NW0pRKoBwqUqH46I3axFIxc%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1468098188", oauth_token="****", oauth_verifier="****", oauth_version="1.0"


    Die Signature-Base war auch dieselbe:

    Quellcode

    1. ​// 1. API
    2. POST&https%3A%2F%2Foauth-api.beatport.com%2Fidentity%2F1%2Foauth%2Faccess-token&oauth_callback%3Doob%26oauth_consumer_key%3D****%26oauth_nonce%3DOfiiaUye%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1468098188%26oauth_token%3D****%26oauth_verifier%3D****%26oauth_version%3D1.0
    3. // Meine API
    4. POST&https%3A%2F%2Foauth-api.beatport.com%2Fidentity%2F1%2Foauth%2Faccess-token&oauth_callback%3Doob%26oauth_consumer_key%3D****%26oauth_nonce%3DOfiiaUye%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1468098188%26oauth_token%3D****%26oauth_verifier%3D****%26oauth_version%3D1.0


    Habe schon Stunden daran rumprobiert, aber kam nie auf das Ergebnis der 1. API.
    Vielleicht mache ich nur einen kleinen Fehler, den ich aber nicht entdecke.

    Wäre sehr hilfreich, wenn jemand den Fehler entdecken würde und ich endlich das Programm weiterschreiben kann :)
    Wenn noch weitere Fragen da sind, fragt mich gerne.

    ~ Alex-Digital
    ~ Alex-Digital :D

    if(!Internet.VBP.Get<User>("Alex-Digital").IsOnline) this.Close(); :D
    Kann es sein das die differenz der Timestamps nicht so gering ist wie erforderlich?

    https://oauth-api.beatport.com/ schrieb:


    Note: Oauth requests contain a nonce
    value as well as a timestamp. In order to prevent replays of api
    requests the timestamps need to match our servers as closely as
    possible.
    And i think to myself... what a wonderfuL World!
    Das sollte aber kein Problem darstellen, da ich bei der Autorisierung keinen richtigen Callback drin habe.
    Habe es schon versucht, das mit einem Webbrowser zu machen, aber dieser buggt mit dem Design herum und möchte ungern für so ein Projekt etwas wie Gecko installieren. Ich versuche es später mal mit Awesomium oder so, aber das sollte nicht so eine große Rolle spielen, da es ja bei der 1. Api genauso funktioniert hat und dort der Timestamp auch genauso lang ist wie bei meiner.
    ~ Alex-Digital :D

    if(!Internet.VBP.Get<User>("Alex-Digital").IsOnline) this.Close(); :D
    Ich hätte ebenfalls sehr viel Interesse an der Lösung dieses Problems...
    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!