2

I'm trying to authenticate a user on my app through their Twitch login. I cannot seem to get request.post() right (using request-promise). I've tried many different variations and I'm usually getting "Unhandled rejection" in the server logs. The Twitch API guide for this is here. The POST response should be JSON. Here is my latest version:

const twitchATParams =
'?client_id=' + twitchAppClientId +
'&client_secret=' + twitchClientSecret +
'&code=' + code +
'&grant_type=authorization_code' +
'&redirect_uri=' + twitchAppRedirect;

request.post(twitchATRequestUrl + twitchATParams)
.then((accessTokenResponse) => {
    const accessToken = accessTokenResponse.access_token;
    console.log('Got an access token: ' + accessToken);
    res.status(200).send('Got an access token: ' + accessToken);
  })
  .catch((error) => {
    res.status(error.statusCode).send(error.error.error_description);
  });

I've also tried this:

request.post({
url:     twitchATRequestUrl,
form:    { client_id: twitchAppClientId,
           client_secret: twitchClientSecret,
           code: code,
           grant_type: "authorization_code",
           redirect_uri:  twitchAppRedirect}
}, function(error, accessTokenResponse, body){
  const accessToken = accessTokenResponse.access_token;
  console.log('Got an access token: ' + accessToken);
  res.status(200).send('Got an access token: ' + accessToken);
});

This is what the Twitch API Guide says I need to do, I think I'm having trouble translating this into JavaScript:

POST https://id.twitch.tv/oauth2/token
  ?client_id=<your client ID>
  &client_secret=<your client secret>
  &code=<authorization code received above>
  &grant_type=authorization_code
  &redirect_uri=<your registered redirect URI>

UPDATE: The app is hosted on Firebase using Cloud Functions. Maybe this is affecting my request?

UPDATE 2: According to this: Deployed Firebase Function Cannot Execute HTTP GET to external API? I can only make external API requests on a Firebase paid plan. I'm assuming this is my issue. I will upgrade to the pay-as-you-go plan (which actually provides a lot of data for free) and try this again and post my results here.

Nick H
  • 217
  • 4
  • 19

3 Answers3

0

I don't know what library you are using, but the last example is very similar with this version: https://www.npmjs.com/package/request.

If is that version the problem is that you are mapping incorrectly the params of your callback. The accessTokenResponse var is the response object not your JSON that you want. You must parse the body param like this.

function(error, response, body){
    console.log(JSON.parse(body));
}
roag92
  • 146
  • 1
  • 6
  • I'm using request-promise but it should include all normal "request" functions too right? I tried what you suggested but I still get the Unhandled Rejection error, and it also says "SyntaxError: Unexpected token u in JSON at position 0" and "RequestError: Error: getaddrinfo ENOTFOUND id.twitch.tv id.twitch.tv:443 at new RequestError". I seems like to there's something wrong with the POST request to Twitch's servers. – Nick H Oct 13 '18 at 11:44
  • I've updated the question with the Twitch API Guide info about this POST request to help clarify. – Nick H Oct 13 '18 at 11:45
  • The error that you mentionated is related with the DNS resolution. I read in the other comment that you are using Cloud Functions, right? So, probably is the way to expose the Cloud Function let me try it. – roag92 Oct 13 '18 at 17:30
  • Nevermind is your current plan of Firebase to do request to another API you must chose a non-free plan. https://stackoverflow.com/questions/42774807/cloud-functions-for-firebase-getaddrinfo-enotfound – roag92 Oct 13 '18 at 17:34
0

I think the way you used request-promise is wrong. Try this instead.

var rp = require('request-promise');

Inside API function:

const twitchATParams =
'?client_id=' + twitchAppClientId +
'&client_secret=' + twitchClientSecret +
'&code=' + code +
'&grant_type=authorization_code' +
'&redirect_uri=' + twitchAppRedirect;


var options = {
    method: 'POST',
    uri: twitchATRequestUrl + twitchATParams,
    /* qs: {
        access_token: 'xxxxx xxxxx' // -> uri + '?access_token=xxxxx%20xxxxx'
    }, */ //you can pass params here too
    json: true // Automatically stringifies the body to JSON
};

rp(options)
    .then(function (response) {
        res.status(200).send('Got an access token: ' + response.accessToken);
    })
    .catch(function (err) {
        // Post failed...
    });
hdk
  • 720
  • 2
  • 9
  • 19
  • Tried this too. Still get "Unhandled rejection" and "RangeError: Invalid status code: 0 at ServerResponse.writeHead". Could this have anything to do with the fact that this app is hosted on Firebase using Cloud Functions? Is that somehow affecting the request or response? – Nick H Oct 13 '18 at 13:01
  • Did you try calling the api using a client like Postman? Did it work as expected? – hdk Oct 13 '18 at 13:06
  • I'm not familiar with Postman. I'm able to call the API and get a response for the first step of this Oauth authentication, I just don't have that code in my example. It's only the second step, which this questions is about, that is giving me issues. – Nick H Oct 13 '18 at 13:15
0

Solved. Turns out I did need a paid plan (Blaze, pay as you go) to access external APIs. I upgraded and that basically solved the issue. It allowed me to see a new error code: StatusCodeError: 400 - "{\"status\":400,\"message\":\"Parameter redirect_uri does not match registered URI\"} so I discovered my redirect url in my code was missing "/callback" (the OAuth Redirect URL on the Twitch app management settings had this "/callback" at the end).

I was also able to use these two blocks of code to successfully get the access token:

const twitchTokenPayload = {
  client_id: twitchAppClientId,
  client_secret: twitchClientSecret,
  code: code,
  grant_type: 'authorization_code',
  redirect_uri: twitchAppRedirect,
};

request.post(twitchATRequestUrl, { json: twitchTokenPayload })
  .then((accessTokenResponse) => {
    const accessToken = accessTokenResponse.access_token;
    console.log('Got an access token: ' + accessToken);
    res.status(200).send('Got an access token: ' + accessToken);
  })
  .catch((error) => {
    console.log('Caught error: ' + error.error.error_description);
    res.status(error.statusCode).send(error.error.error_description);
  });

And this also worked:

request.post({
url:     twitchATRequestUrl,
form:    { client_id: twitchAppClientId,
           client_secret: twitchClientSecret,
           code: code,
           grant_type: "authorization_code",
           redirect_uri:  twitchAppRedirect}
}, function(error, response, body){
  console.log(JSON.parse(body));
  const jsonStuff = JSON.parse(body);
  const accessToken = jsonStuff.access_token;
  console.log('Got an access token: ' + accessToken);
  res.status(200).send('Got an access token: ' + accessToken);
});
Nick H
  • 217
  • 4
  • 19