Search code examples
javascripttypescriptpaypalxmlhttprequestcors

Receive OAuth2 Token from XHR Request


I am trying to get an OAuth2 Token via XHR-Request in TypeScript like mentioned on this side(https://developer.paypal.com/docs/integration/direct/make-your-first-call/). My code so far:

        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();

Sadly I keep getting 401 as response. I already tried the curl command with the same clientID and secret, which worked for me. Can someone tell me whats wrong with me code?


Solution

  • The request you’re getting the 401 response for isn’t your POST request but instead the CORS preflight OPTIONS request that the browser automatically does on its own.

    https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests has details about what triggers browsers to do a preflight but what it comes down to in this specific case is that you’re adding grant_type, Username, and Password request headers to the request in order to do the authentication needed—but your browser makes the preflight OPTIONS request without those headers (because the purpose of the preflight is to ask the server to indicate whether it’s OK with receiving requests that include those request headers).

    And so what happens is the following (reproduced using curl just for illustration purposes):

    $ 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
    

    That is, the https://api.sandbox.paypal.com/v1/oauth2/token endpoint apparently requires authentication even for OPTIONS requests. Because it does, there’s no way you can make POST requests to that endpoint directly from your frontend JavaScript code running in a browser.

    So you’ll instead need to make the request from your backend code.