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.
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:
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.
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!
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?
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.