如何通过使用OAuth 1.0身份验证的Upwork API,通过jQuery AJAX请求JSONP文件?

10

我需要通过jQuery AJAX从Upwork API请求一个JSONP文件。Upwork API使用OAuth 1.0认证。

我对Oauth是新手,但在过去几天里一直在阅读它,我大致了解它的工作原理,但在这种特定的情况/环境下实现它非常困难。我已经为此苦恼了几天,而且Upwork API支持并没有提供太多帮助 :(

我需要通过OAuth 1.0的所有必要步骤,并将OAuth参数传递给请求的URL。请帮忙!

到目前为止,我所做的:

// My Upwork API key and secret
var api_key = 'xxx',
    api_secret = 'xxx';


// TO-DO
// OAuth 1.0 authentication


// TO-DO
// required oauth parameters
// https://developers.upwork.com/?lang=node#authentication_required-oauth-10-parameters
var oauth_consumer_key = '',
    oauth_signature = '',
    oauth_nonce = '',
    oauth_signature_method = '',
    oauth_timestamp = '',
    oauth_token = '';


// Compose request url with required oauth parameters
var url  = "https://www.upwork.com/api/profiles/v2/search/jobs.json?q=java&callback=?";
url += "&oauth_consumer_key="+oauth_consumer_key;
url += "&oauth_signature="+oauth_signature;
url += "&oauth_nonce="+oauth_nonce;
url += "&oauth_signature_method="+oauth_signature_method;
url += "&oauth_timestamp="+oauth_timestamp;
url += "&oauth_token="+oauth_token;


// Ajax request
// https://developers.upwork.com/?lang=node#getting-started_cross-domain-requests
$.ajax({
  url: url,
  dataType: 'JSONP',
  success:function(json){
    alert("Success: "+json.server_time);
  },
  error:function(){
    alert("Error");
  },
});

CodePen: http://codepen.io/nunoarruda/pen/xZBEzB?editors=1010

谢谢提前!


请问您能分享一下服务器详情(URL)吗? - saravanakumar
1
为什么不使用像 https://github.com/ddo/oauth-1.0a 这样的库来完成呢? - Benvorth
@Benvorth 我试过那个好几次了,但没能让它工作。此外,看起来那个框架只处理 Oauth 流程的一部分,即在你已经拥有令牌密钥和令牌密钥的情况下处理流程。 - nunoarruda
@DanielProtopopov 我理解Oauth的整个过程,但在这种情况下实现它确实很困难,我已经阅读并反复阅读了文档。也许Upwork API文档不是很好,但我认为Oauth缺乏绝对初学者的详细指南。我会查看那个链接,谢谢! - nunoarruda
@BorisSerebrov 谢谢你的努力。我会在有时间的时候给出适当的反馈。 - nunoarruda
显示剩余3条评论
1个回答

4

TLDR 为了确保下面的代码示例和我的结论更加清晰,我将从OAuth 1.0流程描述开始。

如果您已经理解OAuth过程,请跳到代码部分。

OAuth 1.0流程

以下术语可能与官方术语不同,但希望能使事情更加清晰:

  • App - 您的应用程序
  • Service - 您请求数据的服务
  • User - 提供访问其由Service存储的数据的用户

准备工作。在Service中注册您的App

您将获得用于启动Oauth过程的客户端密钥和密钥。

在Upwork的情况下,您可以在此处执行此操作 - https://www.upwork.com/services/api/apply

步骤1.获取临时oauth令牌。

您的App向Service发出此请求。 您的App传递client key,因此Service知道谁在请求。

请求使用client secret进行签名,Service也有它,并且可以验证它是否实际上是来自您的App而不是从窃取您的客户端密钥的其他人(这就是为什么您不应向任何人显示您的密钥的原因)。

服务器返回temporary oauth token + temporary oauth secret

在Upwork的情况下,您将此请求发送到https://www.upwork.com/api/auth/v1/oauth/token/request

步骤2.请求用户授权访问。

您的应用程序只需将用户重定向到Service提供的特殊URL。

服务显示一个对话框,用户可以在其中为您的应用程序提供访问权限。 这个特殊的URL包括第1步中的temporary token,因此Service知道哪个应用程序请求访问权限。

如果您有一个Web应用程序,只需在浏览器中打开此特殊的URL。 然后,Service使用oauth_callback(要将用户重定向回的URL)重定向回您的App。 服务还将oauth_verifier传递给oauth_callback URL。

如果您有一个桌面应用程序,则应启动浏览器,并且Service可以将oauth_verifier显示为字符串,以便用户可以手动复制它并粘贴回您的App。在这种情况下,您将oauth_calback设置为特殊的oob(out-of-band)值。 此部分(不包括重定向回)在规范中没有严格描述,因此详细信息取决于Service。 它可能根本不受支持,或以其他方式受支持。

在Upwork的情况下,您将用户发送到URL https://www.upwork.com/services/api/auth?oauth_token={temporary token}。
第三步。获取实际的OAuth访问令牌。
您的应用程序将步骤1中的临时令牌和步骤2中的OAuth验证器发送给服务。 该请求再次签名,但这次使用客户端密钥和临时令牌密钥。 服务响应访问令牌+密钥。
在Upwork的情况下,URL为https://www.upwork.com/api/auth/v1/oauth/token/access 这些是获取实际访问令牌并开始使用服务API的3个步骤。 规范中的示例也很好,请查看。 还要注意,OAuth 1.0不能在100%客户端应用程序中安全使用。 在步骤1中,您需要使用私有的客户端密钥,不应为任何人所知(因此您不得将其放入客户端代码中)。 在步骤2中,服务将重定向浏览器回到oauth_callback,您无法在客户端处理它。
从技术上讲,如果您使用没有回调的方案(例如桌面应用程序),则可以在客户端使用oauth。 在这种情况下,用户将需要手动将验证器复制回您的应用程序。 服务还应支持此场景(Upwork不支持,请参见下文)。
第4步。使用服务API
现在,一旦您获得访问令牌,就可以发出API请求以获取数据,这里发送步骤3中的客户端密钥和访问令牌。 请求使用客户端密钥+访问令牌密钥进行签名。
该过程最复杂的部分是请求签名,在规范中详细介绍了它,但最好使用库来完成此操作。 oauth-1.0a允许您在node.js和客户端javascript中签署请求。 您仍然需要从应用程序执行oauth步骤,库将只帮助您签名。
代码
我从浏览器JavaScript测试了Step 1,但Upwork不支持此场景。 如果我使用ajax发送常规POST请求,则返回“Access-Control-Allow-Originerror。如果我尝试使用JSONP`进行此请求,Upwork会响应404错误。

因此,api/auth/v1/oauth/token/request端点不支持JSONP

步骤1-3应该使用服务器端完成(客户端身份验证可能不安全)。

以下是令牌请求的示例(Step 1):

oauthTest.step1_tempToken = function() {
    var request_data = {
        url: 'https://www.upwork.com/api/auth/v1/oauth/token/request',
        method: 'POST',
        data: {}
    };
    request({
        url: request_data.url,
        method: request_data.method,
        form: oauthTest.oauth.authorize(request_data) // no token yet
    }, function(error, response, body) {
        var data = qs.parse(body);
        console.log(data);
    });
};

完整代码在这里
请注意,Upwork有nodejs库,但我没有使用它来手动完成所有事情。请求是使用oauth-1.0a进行签名的。
在浏览器中执行步骤2,只需打开网址 'https://www.upwork.com/services/api/auth?oauth_token=xxx' 并获取oauth验证器。在现实生活中,您的应用程序将指定oauth_callback参数,Upwork将向您的应用程序发送oauth验证器。在此示例中,我只是手动从浏览器复制它并传递到下一步。
有了oauth验证器,您可以获取永久访问令牌(Step 3):
oauthTest.step3_accessToken = function(oauth_verifier) {
    var request_data = {
        url: 'https://www.upwork.com/api/auth/v1/oauth/token/access',
        method: 'POST',
        data: {
          oauth_verifier: oauth_verifier
        }
    };
    request({
        url: request_data.url,
        method: request_data.method,
        form: oauthTest.oauth.authorize(request_data, oauthTest.tempToken) // use the temp token
    }, function(error, response, body) {
        var data = qs.parse(body);
        console.log(data);
    });
};

最后,您可以使用API中的第四步(再次强调,这是服务器端代码):

oauthTest.queryAPI = function() {
    var request_data = {
        url: 'https://www.upwork.com/api/profiles/v2/search/jobs.json',
        method: 'GET',
        data: {
          'q': 'java'
        }
    };
    request({
        url: request_data.url,
        method: request_data.method,
        qs: oauthTest.oauth.authorize(request_data, oauthTest.accessToken) // use the access token
    }, function(error, response, body) {
        console.log(body);
    });
};

可以在客户端使用API(尽管这不好,因为您需要将访问令牌和密钥放入代码中)。

解决方案有些棘手,因为文档(https://developers.upwork.com/?lang=node#getting-started_cross-domain-requests)不完整,也不完全正确。

文档中说要在请求中添加callback=?,但是当您设置JSONP数据类型时,jQuery会自动添加此参数。此外,参数值被设置为一些随机字符串,因此我认为此参数不应该被签名,但实际上它应该:

function queryAPI(public, secret) {
    var accessToken = {
        public: public,
        secret: secret
    }
    var request_data = {
        url: 'https://www.upwork.com/api/profiles/v2/search/jobs.json',
        method: 'GET',
        data: {
          'q': 'java',
          'callback': 'jsoncallback'
        }
    };

    // It looks like a bug on the Upwork side, the `callback` parameter is usually
    // selected randomly by jQuery, so server side should skip it from the signature
    // validation, but it doesn't, so we sign the request with `callback` parameter
    // and then remove it from data, because it this parameter is automatically added
    // by jQuery, we also set the static value for callback - 'jsoncallback`
    var data = oauth.authorize(request_data, accessToken);
    delete data.callback;

    // Ajax request
    // https://developers.upwork.com/?lang=node#getting-started_cross-domain-requests
    $.ajax({
      // url: url,
      url: request_data.url,
      dataType: 'JSONP',
      jsonpCallback: 'jsoncallback',
      // here the data will contain 'q=java' as well as all the oauth parameters
      // the request type will be GET (since this is JSONP), so all parameters will
      // be converted to the query string
      // you can check the URL in the developer console, in the list of network requests
      //data: oauth.authorize(request_data, accessToken),
      data: data,
      cache: true, // this removes the '_' parameter
      success:function(json){
        console.log(json);
      },
      error: function(error){
        console.log(error);
      },
    });
};

无论如何,这是不安全的。由于您需要服务器端进行Oauth,因此您也可以使用它来向API发出请求并将结果返回到客户端。

如何使用代码示例

获取nodejs-upwork-oauth文件夹的副本,执行npm install并启动node.js控制台:

$ node
> oauthTest = require('./server')
> oauthTest.step1_tempToken()
> // wait for the result
{ public: 'xxxx',
  secret: 'yyyy' }
> // copy the public temp access token
> // don't exit it yet
>

现在在浏览器中打开test.html,并打开JS控制台,运行:
> step2_askUser('temp_access_token_here')
> // it will open the upwork auth page in new tab
Application authorized

jobs-alert has been authorized.
Your oauth_verifier=zzzz

You can close this window and return to your application.
> // authorize there and copy the oauth_verifier

回到Node.js控制台:

> oauthTest.step3_accessToken('oauth verifier here')
> // wait for the result
{ public: 'nnnnn',
  secret: 'kkkkk' }
> oauthTest.queryAPI()
> // see the query result

然后回到浏览器:

> queryAPI('access token public', 'access token secret')
< Object {server_time: 1456301893, auth_user: Object, profile_access: "public,odesk", jobs: Array[10], paging: Object}

使用客户端请求更新了答案。 - Borys Serebrov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接