网站搜索

javascript - 从 XHR 请求接收 OAuth2 令牌


我正在尝试通过 TypeScript 中的 XHR 请求获取 OAuth2 令牌,就像在这个页面上提到的(https://developer.paypal.com/docs/integration/direct/make-your-first-call/)。我到目前为止的代码如下:

        var clientID = <clientID>;
        var secret = <mySecret>;

        var oReq = new XMLHttpRequest();
        oReq.open("POST", "https://api.sandbox.paypal.com/v1/oauth2/token", true);

        oReq.setRequestHeader('grant_type', "client_credentials");
        oReq.setRequestHeader('Username', this.clientID);
        oReq.setRequestHeader('Password', this.secret);
        oReq.setRequestHeader('Content-type', "application/x-www-form-urlencoded");
        oReq.setRequestHeader('Accept', "application/json");
        oReq.setRequestHeader('Accept-Language', "en_US");         
        oReq.onreadystatechange = function () {
            if (oReq.readyState === 4) {
                if (oReq.status === 200) {
                    console.log(oReq.responseText);
                } else { 
                    console.log("Error: " + oReq.status);
                } 
            }     
        console.log("Status: " + oReq.status);
        }; 
        console.log("Status: " + oReq.status);

        oReq.send();

可悲的是,我一直收到 401 作为响应。我已经使用相同的 clientID 和 secret 尝试了 curl 命令,这对我有效。有人能告诉我我的代码出了什么问题吗?

答案#1

你收到 401 响应的请求不是你的 POST 请求,而是浏览器自动执行的 CORS 预检 OPTIONS 请求。

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS#Preflighted_requests 详细介绍了什么会触发浏览器进行预检,但在这个特定情况下,问题归结为你为了进行所需的认证,向请求中添加了 grant_type、Username 和 Password 请求头——但是你的浏览器会在发起预检 OPTIONS 请求时不带这些请求头(因为预检的目的就是询问服务器是否允许接收包含这些请求头的请求)。

所以发生的情况如下(仅使用curl进行说明性演示):

curl -X OPTIONS -i -H "Origin: http://example.com" \
    'https://api.sandbox.paypal.com/v1/oauth2/token'

HTTP/1.1 401 Unauthorized
Date: Wed, 09 Aug 2017 09:08:32 GMT
Server: Apache
paypal-debug-id: f8963606c5654
Access-Control-Allow-Origin: http://example.com
Access-Control-Expose-Headers: False
HTTP_X_PP_AZ_LOCATOR: sandbox.slc
Paypal-Debug-Id: f8963606c5654
Set-Cookie: X-PP-SILOVER=name%3DSANDBOX3.API.1%26silo_version%3D1880%26app%3Dapiplatformproxyserv%26TIME%3D282167897%26HTTP_X_PP_AZ_LOCATOR%3Dsandbox.slc; Expires=Wed, 09 Aug 2017 09:38:32 GMT; domain=.paypal.com; path=/; Secure; HttpOnly
Set-Cookie: X-PP-SILOVER=; Expires=Thu, 01 Jan 1970 00:00:01 GMT
Content-Length: 0
Connection: close
Content-Type: text/plain; charset=ISO-8859-1

也就是说,https://api.sandbox.paypal.com/v1/oauth2/token 端点显然即使对于 OPTIONS 请求也需要身份验证。正因为如此,你无法直接从在浏览器中运行的前端 JavaScript 代码向该端点发起 POST 请求。

所以你将需要从你的后端代码发出请求。