为了使BitBucket OAuth API正常工作,我一直在桌子上敲打头整整一周,甚至无法获得一个请求令牌来挽救自己的生命。我在此处创建的代码可用于LinkedIn,但BitBucket始终返回HTTP状态代码400,其中包含一条非常冗长且有用的消息:“无法验证OAuth请求”。
这是一个简单的代码(50行,不能缩短它),是因为我需要手动实现OAuth,否则我不会在这里询问并使用其他外部库,但是由于公司的要求,我不是允许在该项目中使用外部库。我看不出有什么问题,1.0a并不难,获取请求令牌的时间不应该花那么长时间。有什么事吗
我还检查了我的时间戳,它很好,针对pool.ntp.org的w32tm.exe返回+30或类似的时间。我还尝试了向UtcNow时间戳添加和删除30分钟的操作,但均未成功,但我的时钟已正确同步(与本地时间和正确的GMT值(GMT -4:30)同步),因此完全没有意义。
可能是因为我在公司防火墙(Forefront)后面吗?但是,为什么LinkedIn的API调用有效而BitBucket不起作用?我还阅读了很多文档,例如OAuth圣经,RFC,官方文档等。当然,在询问之前对SO进行了广泛的搜索,并在点击“相似问题”面板中查看了所有链接“发布您的问题”按钮。
这是简单的代码(C#):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Security.Cryptography;
namespace OAuthTest1
{
class Program
{
static void Main(string[] args)
{
String key = "KEY";
String secret = "SECRET";
String requestUrl = "https://bitbucket.org/api/1.0/oauth/request_token";
string sigBaseStringParams = "oauth_consumer_key=" + key;
sigBaseStringParams += "&" + "oauth_nonce=" + GetNonce();
sigBaseStringParams += "&" + "oauth_signature_method=" + "HMAC-SHA1";
sigBaseStringParams += "&" + "oauth_timestamp=" + GetTimeStamp();
sigBaseStringParams += "&" + "oauth_callback=http%3A%2F%2Flocal%3Fdump";
sigBaseStringParams += "&" + "oauth_version=1.0";
string sigBaseString = "POST&";
sigBaseString += Uri.EscapeDataString(requestUrl) + "&" + Uri.EscapeDataString(sigBaseStringParams);
string signature = GetSignature(sigBaseString, secret);
Console.WriteLine(PostData(requestUrl, sigBaseStringParams + "&oauth_signature=" + Uri.EscapeDataString(signature)));
Console.ReadKey();
}
public static string GetNonce()
{
return new Random().Next(1000000000).ToString();
}
public static string GetTimeStamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
public static String PostData(string url, string postData)
{
WebClient w = new WebClient();
return w.UploadString(url, postData);
}
public static string GetSignature(string sigBaseString, string consumerSecretKey, string requestTokenSecretKey = null)
{
HMACSHA1 hmacsha1 = new HMACSHA1();
String signingKey = string.Format("{0}&{1}", consumerSecretKey, !string.IsNullOrEmpty(requestTokenSecretKey) ? requestTokenSecretKey : "");
hmacsha1.Key = Encoding.UTF8.GetBytes(signingKey);
return Convert.ToBase64String(hmacsha1.ComputeHash(Encoding.UTF8.GetBytes(sigBaseString)));
}
}
}
编辑:这是提琴手和浏览器捕获的请求:
提前致谢!
编辑2:我使用n0741337的建议提供的OAuthBase.cs类进行了新的测试,但是,该类不符合1.0A规范,因为它要求使用回调方法(至少Bitbucket足以说明这一点)此类参数是必需的),因此我必须对其进行修改,以便它在基本签名字符串中包含回调参数(未经编码的原始格式)。尽管结果相同(我认为显示密钥并不重要,因为无论如何您都不会看到我的秘密密钥),以下是捕获的结果:
这是签名基本字符串:
GET&https%3A%2F%2Fbitbucket.org%2Fapi%2F1.0%2Foauth%2Frequest_token&oauth_callback%3Dhttp%3A%2F%2Flocalhost%3A4000%2F%26oauth_consumer_key%3Dkey%26oauth_nonce%3D8231861%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1423671576%26oauth_version%3D1.0
Also, to put my code in doubt, one can also use this class I found which someone already adapted for Twitter (though Twitter is blocked in my workplace so I can't test against it), but the results are the same.
Here's the new code I made which uses such classes:
using System;
using System.Net;
using System.Security.Cryptography;
using OAuth;
namespace OAuthTest1
{
public class oauth2
{
public static void Run()
{
OAuthBase a = new OAuthBase();
String nonce = a.GenerateNonce();
String ts = a.GenerateTimeStamp();
String key = "key";
String secret = "secret";
String requestUrl = "https://bitbucket.org/api/1.0/oauth/request_token";
String normalizedUrl, normalizedArgs;
String sigBase = a.GenerateSignatureBase(
new Uri(requestUrl),
key,
null,
secret,
"http://localhost:4000/",
"GET",
ts,
nonce,
"HMAC-SHA1",
out normalizedUrl,
out normalizedArgs
);
String sig = a.GenerateSignatureUsingHash(sigBase, new HMACSHA1());
String GETArgs = String.Empty;
GETArgs += "oauth_consumer_key=" + key;
GETArgs += "&oauth_nonce=" + nonce;
GETArgs += "&oauth_signature_method=HMAC-SHA1";
GETArgs += "&oauth_timestamp=" + ts;
GETArgs += "&oauth_version=1.0";
GETArgs += "&oauth_callback=" + a.UrlEncode("http://localhost:4000/");
GETArgs += "&oauth_signature=" + sig;
WebClient w = new WebClient();
Console.WriteLine(w.DownloadString(requestUrl + "?" + GETArgs));
Console.ReadKey();
}
}
}
Question for Edit 2: Does anyone know of an app which connects to Bitbucket's service through this method so I can run Fiddler and see what it is sending? If I could see some output I could at least replicate the flow :/ I've tried against SourceTree but it doesn't work very well.
Edit 3: By AZ.'s suggestion I changed the timestamp generation code with this, but it doesn't work anyways :(. Timestamp values look okay though, there's only a slight difference of 5 seconds between my timestamp and the server's:
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
String timeStamp = Convert.ToInt64(ts.TotalSeconds).ToString();
Also, I notice the signature contains a "+" which should've been encoded to %20, which I did when I noticed this after editing the question and it doesn't work either, just FYI.
There are 2 things wrong with your program:
Content-Type: application/x-www-form-urlencoded
header, which makes the POST body unintelligible (it's generally recommended to use the Authorization request header).oauth_callback
您会弄错参数的顺序(http://oauth.net/core/1.0/#anchor14)这是使程序正常运行的补丁:
--- Program.cs 2015-02-23 23:13:03.000000000 -0800
+++ Program3.cs 2015-02-23 23:20:37.000000000 -0800
@@ -15,11 +15,12 @@
String secret = "SECRET";
String requestUrl = "https://bitbucket.org/api/1.0/oauth/request_token";
- string sigBaseStringParams = "oauth_consumer_key=" + key;
+ string sigBaseStringParams = "";
+ sigBaseStringParams += "oauth_callback=http%3A%2F%2Flocal%3Fdump";
+ sigBaseStringParams += "&" + "oauth_consumer_key=" + key;
sigBaseStringParams += "&" + "oauth_nonce=" + GetNonce();
sigBaseStringParams += "&" + "oauth_signature_method=" + "HMAC-SHA1";
sigBaseStringParams += "&" + "oauth_timestamp=" + GetTimeStamp();
- sigBaseStringParams += "&" + "oauth_callback=http%3A%2F%2Flocal%3Fdump";
sigBaseStringParams += "&" + "oauth_version=1.0";
string sigBaseString = "POST&";
sigBaseString += Uri.EscapeDataString(requestUrl) + "&" + Uri.EscapeDataString(sigBaseStringParams);
@@ -44,6 +45,7 @@
public static String PostData(string url, string postData)
{
WebClient w = new WebClient();
+ w.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
return w.UploadString(url, postData);
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句