Delphi XE2 - 总是得到401 Unauthorized()试图从Intuit获取OAuth访问令牌

时间:2014-02-12 19:57:29

标签: delphi oauth delphi-xe2 intuit-partner-platform

我正在与Intuit一起开展OAuth舞蹈的最后一步。

这是一个Delphi XE2桌面应用程序,我使用的是Indy组件。

我成功从Intuit获取请求令牌,并且在我授权我的应用后,他们网站的回调正确地将我重定向到我们的网络服务。

使用Intuit中返回的验证程序构建我的请求以获取访问令牌。

当我拨打Intuit电话时,我总是收到 HTTP / 1.1 401 Unauthorized 错误。如果我将构建的网址发布到Chrome浏览器中,则会收到“ oauth_problem = signature_invalid ”消息。

显然,我没有充分掌握OAuth的东西,但我很确定我的签名是正确的。我在这里使用Google的签名检查工具:http://oauth.googlecode.com/svn/code/javascript/example/signature.html,我的签名总是匹配。我的签名基础也匹配。

以下是我用来生成签名的键/值以及我从结果中生成的URL:

http method: GET
URL: https://oauth.intuit.com/oauth/v1/get_access_token
singing method: HMAC-SHA1
version: 1.0
ConsumerKey: qyprdGItCTwCAbvx3qNSJR5vDnCOVX
ConsumerSecret: 7IxtFa7aYU2O0PvMGSKsEqaZRvQFoZFaPCtNqg2D
RequestToken: qyprdsHZZft4b2Xskx7dMh1qKOEDw6hrns2Dh7KhN7H8csZt
TokenSecret: AWYbBHrhYhUTYlD1kdPRal2NXRwWMo8qDZ7HDBTQ
TimeStamp: 1392232483
nonce: 3EE3F797E97810F1F0CDF866BEB1AA68

如果我将以上所有内容插入Google的签名工具中,我会从代码中获得相同的签名。我的签名库也与我从Google工具中获得的一致。

Signature Base: GET&https%3A%2F%2Foauth.intuit.com%2Foauth%2Fv1%2Fget_access_token&oauth_consumer_key%3DqyprdGItCTwCAbvx3qNSJR5vDnCOVX%26oauth_nonce%3D3EE3F797E97810F1F0CDF866BEB1AA68%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1392232483%26oauth_token%3DqyprdsHZZft4b2Xskx7dMh1qKOEDw6hrns2Dh7KhN7H8csZt%26oauth_version%3D1.0
signature : F7m3ii6is226/waoA5wkhrFN/Ak=
signature URL encoded: F7m3ii6is226%2FwaoA5wkhrFN%2FAk%3D

将我在下面的代码中构建的网址粘贴到Chrome浏览器中会给我oauth_problem错误。

URL:  https://oauth.intuit.com/oauth/v1/get_access_token?oauth_consumer_key=qyprdGItCTwCAbvx3qNSJR5vDnCOVX&oauth_token=qyprdsHZZft4b2Xskx7dMh1qKOEDw6hrns2Dh7KhN7H8csZt&oauth_signature_method=HMAC-SHA1&oauth_signature=F7m3ii6is226%2FwaoA5wkhrFN%2FAk%3D&oauth_timestamp=1392232483&oauth_nonce=3EE3F797E97810F1F0CDF866BEB1AA68&oauth_version=1.0&oauth_verifier=dv9xx1q

请记住,在构建get_request_token的URL时,生成我的签名的代码可以正常工作。最后一点是获取最重要的访问令牌和访问令牌秘密,其中事情正在崩溃。

我在oauth.net文档(http://oauth.net/core/1.0a/#auth_step3)中读到,最后一次调用应该通过HTTP POST完成,但我没有看到任何描述帖子正文应该包含的内容,并且Intuit合作伙伴平台上没有任何内容描述POST身体要么所以我正在尝试GET。我已经尝试了一个没有运气的空机身的POST。

感谢任何建议。

干杯! TJ

1 个答案:

答案 0 :(得分:0)

当您在浏览器中输入URL并将其移除时,它会执行GET请求。但是,正如您所提到的,以及您链接的Intuit documentation所说的内容,get_access_token请求应该使用POST请求来完成。

如果您阅读了Intuit文档的section 5.2,它会说:

  

OAuth协议参数以三种方法之一从消费者发送到服务提供商,按优先顺序递减:

     
      
  1. 在OAuth HTTP授权方案中定义的HTTP授权标头中。
  2.   
  3. 作为内容类型为application / x-www-form-urlencoded的HTTP POST请求正文。
  4.   
  5. 添加到查询部分中的URL(由RFC3986第3节定义)。
  6.   

当你应该做#2时,你正在做#3。正确的POST请求看起来像这样:

POST /oauth/v1/get_access_token HTTP/1.1
Host: oauth.intuit.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 307

oauth_consumer_key=qyprdGItCTwCAbvx3qNSJR5vDnCOVX&oauth_token=qyprdsHZZft4b2Xskx7dMh1qKOEDw6hrns2Dh7KhN7H8csZt&oauth_signature_method=HMAC-SHA1&oauth_signature=F7m3ii6is226%2FwaoA5wkhrFN%2FAk%3D&oauth_timestamp=1392232483&oauth_nonce=3EE3F797E97810F1F0CDF866BEB1AA68&oauth_version=1.0&oauth_verifier=dv9xx1q

您提到您正在使用Indy,因此可以使用以下代码发送此类POST请求:

var
  Params: TStringList;
begin
  Params := TStringList.Create;
  try
    Params.Values['oauth_consumer_key'] := 'qyprdGItCTwCAbvx3qNSJR5vDnCOVX';
    Params.Values['oauth_token'] := 'qyprdsHZZft4b2Xskx7dMh1qKOEDw6hrns2Dh7KhN7H8csZt';
    Params.Values['oauth_signature_method'] := 'HMAC-SHA1';
    Params.Values['oauth_signature'] := 'F7m3ii6is226/waoA5wkhrFN/Ak='; // <-- pay attention here!
    Params.Values['oauth_timestamp'] := '1392232483';
    Params.Values['oauth_nonce'] := '3EE3F797E97810F1F0CDF866BEB1AA68';
    Params.Values['oauth_version'] := '1.0';
    Params.Values['oauth_verifier'] := 'dv9xx1q';

    IdHTTP.Post('https://oauth.intuit.com/oauth/v1/get_access_token', Params);
  finally
    Params.Free;
  end;
  ...
end;

特别注意oauth_signature值。发送到服务器的内容必须是url编码的:

F7m3ii6is226%2FwaoA5wkhrFN%2FAk%3D

默认情况下,TIdHTTP会在准备TStringList请求时为您的POST数据进行网址编码,因此请勿将网址编码的数据放入TStringList

F7m3ii6is226/waoA5wkhrFN/Ak=

但是,如果要将url编码的数据放在TStringList中,则必须禁用hoForceEncodeParams属性中的TIdHTTP.HTTPOptions标记。 TIdHTTP仍然会将TStringList数据作为name-value对发送,每对中包含&个字符,但每对的名称和值将按原样发送,而不是网址 - 由TIdHTTP编码。