Node.js OAuth 2.0 with the Concur API, get access token returns Error: Invalid Protocol: null

Using two separate approaches, resulting in two different problems. I'll refer to these as Approach A and Approach B. Both are currently failing for what appears to be two different reasons.

Using the npm libraries passport and passport-oauth2 to successfully complete the first leg of the Concur API's OAuth 2.0 web flow.

login with concur credentials

concur API successfully asks for permission to OAuth

Approach A

For the second leg of the OAuth 2.0 web flow, I've implemented the npm library oauth.

// "code" is the code successfully returned from the first leg of the OAuth
oauth2.getOAuthAccessToken(
  code,
  {'grant_type': 'client_credentials'},
  function (err, access_token, refresh_token, results) {
    if (err) return done(err);
    _accessToken = access_token;
    _refreshToken = refresh_token;
    console.log("my access_token", access_token);
    console.log("my refresh_token", refresh_token);
    next();
  });

But for whatever reason, the Concur API documentation explicitly tells us to use the GET verb for its get-access-token endpoint:

GET https://www.concursolutions.com/net2/oauth2/GetAccessToken.ashx?code=0987654321&client_id=eZByXv2X41cJlC21pSVvRi&client_secret=4EW8e72wOCM2jKL12H5s2ss HTTP 1.1

And, sure enough Approach A results in tears:

Concur API fails to OAuth with POST verb

Worth noting, above is the exact same result obtained when I originally just used the same npm library passport-oauth2 to perform the second leg of the OAuth 2.0 web flow.

Approach B

So in an attempt to gain more control of the second leg of the OAuth 2.0 web flow, I've dug down a layer and implemented the npm library request.

We give this a whirl:

// "code" is the code successfully returned from the first leg of the OAuth
request({
  url: config.concur.tokenURL
    + '?' + [
      'code=' + code,
      'client_id=' + apiConfig.clientId,
      'client_secret=' + apiConfig.clientSecret
  ].join('&')
}, function (err, response, body) {
  if (err) return done(err);
   // etc
});

Tears!

failure to OAuth

Because for whatever reason, the Concur API documentation explicitly tells us to make an HTTP call to an HTTPS address:

GET https://www.concursolutions.com/net2/oauth2/GetAccessToken.ashx?code=0987654321&client_id=eZByXv2X41cJlC21pSVvRi&client_secret=4EW8e72wOCM2jKL12H5s2ss HTTP 1.1

What?

Approach C

Just for fun, I tried switching the address I'm using for the get-access-token to the http:// protocol. And this fixed the issue from Approach B.

To make things weirder, it's an XML endpoint. So we'll use the npm library xml2js to parse the return data.

// "code" is the code successfully returned from the first leg of the OAuth
request({
  url: config.concur.tokenURL
    + '?' + [
      'code=' + code,
      'client_id=' + apiConfig.clientId,
      'client_secret=' + apiConfig.clientSecret
  ].join('&')
}, function (err, response, body) {
  if (err) return done(err.toString());
  xml2js.parseString(body, function (err, result) {
    if ('Error' in result) return done(result);
    _accessToken = result.Access_Token.Token;
    _refreshToken = result.Access_Token.Refresh_Token;
    next();
  });
});

But then, when I make authenticated request to their API (http: or https:) using the token I acquired via http, I again see:

Error: Invalid Protocol: null

I'm mocking this API out of the sprint for now.