Http request error using firebase functions/nodejs - javascript

Ive been trying unsuccessfully to get an http request to the Genius API server running through my cloud functions node.js instance. I am currently stuck getting this 'Server Error' message and am not sure how else to move around this. I am new to http requests, so was wondering if there were any glaring mistakes im making in this request? Or possible ideas for how to get more useful information from the error console
My console log currently outputs:
(node:43068) UnhandledPromiseRejectionWarning: Error: Server error.
at /Users/xxx/Documents/GitHub/xxx/functions/index.js:58:21
at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:43068) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1))
const functions = require('firebase-functions');
const express = require('express')
const app = express()
const port = 3035
var http = require('http');
const https = require('https');
const fetch = require('node-fetch');
app.get('/auth', (req, res) => {
const accessToken ='xxx';
const clientId = 'xxx';
const clientSecret = 'xxx';
const uri = `https://api.genius.com/oauth/authorize`;
const options = {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({
"code": "CODE_FROM_REDIRECT",
"client_id": clientId,
"client_secret": clientSecret,
"redirect_uri": "YOUR_REDIRECT_URI",
"response_type": "code",
"grant_type": "authorization_code"
}),
};
return fetch(uri, options).then((res) => {
if (res.ok) {
return res.json();
} else if (res.status == 409) {
throw new Error('IdP configuration already exists. Update it instead.');
} else {
throw new Error('Server error.');
}
});
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})

In your then handler you are throwing exceptions that are not catched anywhere (at least not in the code you are showing us). Thus, you get an UnhandledPromiseRejectionWarning. And as this is all inside an express routehandler, you are not returning anything to your client calling this route (not even if the authenication call is successful)
app.get('/auth', (reqest, response) => {
const accessToken ='xxx';
const clientId = 'xxx';
const clientSecret = 'xxx';
const uri = `https://api.genius.com/oauth/authorize`;
const options = {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({
"code": "CODE_FROM_REDIRECT",
"client_id": clientId,
"client_secret": clientSecret,
"redirect_uri": "YOUR_REDIRECT_URI",
"response_type": "code",
"grant_type": "authorization_code"
}),
};
fetch(uri, options).then((res) => {
if (res.ok) {
return res.json();
} else if (res.status == 409) {
throw new Error('IdP configuration already exists. Update it instead.');
} else {
throw new Error('Server error.');
}
})
.then(json => {
response.send(json); // will send status 200 and the json as body
})
.catch(e => {
console.log(e);
response.sendStatus(400); //or whatever status code you want to return
});
})

Often you may need to do some tweaks to the data before it's passed to the consumer, for example:
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(response.statusText)
}
return response.json()
})
.then(data => {
return data.data
})
.catch((error: Error) => {
throw error
})

Related

Unable to get the required redirect_uri in react-facebook-login

I'm trying to implement the Facebook OAuth in my express/NodeJS app using authorization code flow. I'm using react-facebook-login node module to fetch the authorization code. In my react app, I could get the authorization code successfully. But in server side, I can't request the access token from the Facebook API as I'm getting an error message "redirect_uri is not identical to the one you used in the OAuth dialog request"
Code in my react app,
facebookLogin = async (signedRequest) => {
return fetch('/api/auth/facebook', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ signedRequest }),
}).then((res) => {
if (res.ok) {
return res.json();
} else {
return Promise.reject(res);
}
});
};
responseFacebook = async (response) => {
try {
if (response['signedRequest']) {
const userProfile = await this.facebookLogin(response['signedRequest']);
console.log(userProfile);
} else {
throw new Error(response.error);
}
} catch (err) {
console.log(err);
}
};
render() {
<FacebookLogin
appId={process.env.FACEBOOK_CLIENT_ID}
fields="name,email"
responseType="code"
redirectUri="http://localhost:3000/"
callback={this.responseFacebook}
/>
In my app.js
const facebookOAuth = require('./config/facebookOAuth');
// facebook oauth route
app.post("/api/auth/facebook", async (req, res) => {
try {
const signedRequest = req.body.signedRequest;
const profile = await facebookOAuth.getProfile(signedRequest);
console.log(profile);
res.send({ profile });
} catch (err) {
console.log(err);
res.status(401).send();
}
});
facebookOAuth.js look like this
const fetch = require('node-fetch');
const getData = async (userId, accessToken) => {
const userData = await fetch(`https://graph.facebook.com/${userId}?fields=name,email&access_token=${accessToken}`, {
method: 'GET'
}).then((res) => {
return res.json();
}).then((userData) => {
return userData;
});
return userData;
};
exports.getProfile = async (signedRequest) => {
const decodedSignedRequest = JSON.parse(Buffer.from((signedRequest.split(".")[1]), 'base64').toString());
const profile = await fetch(`https://graph.facebook.com/oauth/access_token?client_id=${process.env.FACEBOOK_CLIENT_ID}&redirect_uri=${encodeURIComponent('http://localhost:3000/')}&client_secret=${process.env.FACEBOOK_CLIENT_SECRET}&code=${decodedSignedRequest.code}`, {
method: 'GET'
}).then((res) => {
return res.json();
}).then((token) => {
console.log(token);
const userData = getData(decodedSignedRequest.user_id, token.access_token);
return userData;
}).catch((err) => {
console.log(err);
return err;
});
return profile;
}
What I'm getting is this error
"error": {
message: 'Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request',
type: 'OAuthException',
code: 100,
error_subcode: 36008,
fbtrace_id: 'A-YAgSqKbzPR94XL8QjIyHn'
}
I think the problem lies in my redirect_uri. Apparently, the redirect uri I obtained from the Facebook auth dialog is different from the one that I'm passing to the facebook API in my server side (http://localhost:3000/).
I believe there's something to do with the origin parameter of the redirect_uri. Initial auth dialog request uri indicates that it's origin parameter value is something like "origin=localhost:3000/f370b6cb4b5a9c". I don't know why react-facebook-login add some sort of trailing value at the end of origin param.
https://web.facebook.com/v2.3/dialog/oauth?app_id=249141440286033&auth_type=&cbt=1620173773354&channel_url=https://staticxx.facebook.com/x/connect/xd_arbiter/?version=46#cb=f39300d6265e5c4&domain=localhost&origin=http%3A%2F%2Flocalhost%3A3000%2Ff370b6cb4b5a9c&relation=opener&client_id=249141440286033&display=popup&domain=localhost&e2e={}&fallback_redirect_uri=http://localhost:3000/&locale=en_US&logger_id=f1b3fba38c5e31c&origin=1&redirect_uri=https://staticxx.facebook.com/x/connect/xd_arbiter/?version=46#cb=f17641be4cce4d4&domain=localhost&origin=http%3A%2F%2Flocalhost%3A3000%2Ff370b6cb4b5a9c&relation=opener&frame=f3960892790a6d4&response_type=token,signed_request,graph_domain&return_scopes=false&scope=public_profile,email&sdk=joey&version=v2.3
I tried finding everywhere about this but no luck. Anyone has clue about this, much appreciated.
Are you using middleware to parse the body? if you aren't code could be undefined here.
const facebookOAuth = require('./config/facebookOAuth');
// facebook oauth route
app.post("/api/auth/facebook", async (req, res) => {
try {
const code = req.body.code;
const profile = await facebookOAuth.getProfile(code);
console.log(profile);
res.send({ profile });
} catch (err) {
console.log(err);
res.status(401).send();
}
});

Struggling to make an async function wait for a database response

Thank you in advance for taking the time to help me.
I am trying to log users in using a MongoDB backend, I call an async function which makes the fetch call, and returns the login token if I get a 200 response code. :
_initialize = async userAddress => {
const token = await login(userAddress);
console.log("TOKEN:", token);
// Do other stuff with the token
}
the login function looks like this:
export const login = async address => {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ address })
};
let responseCode;
fetch(`___ENDPOINT___`, requestOptions)
.then(response => {
responseCode = response.status;
return(response.json());
})
.then(data => {
if(responseCode == 200){
console.log(data.token)
const token = data.token;
return token;
}
if(responseCode == 400){
if(data.message === "User not yet registered"){
// Do nothing
return;
}
}
})
.catch(error => {
console.log(error)
});
}
The issue that I am having is that the await login() call is not being waited on, instead token is console logged as undefined.
If I remove the await keyword I receive token as :
Promise {<fulfilled>: undefined}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: undefined
I don't get it, why is token undefined? If I log token in the .then block it is returned successfully, but this only occurs after token has been printed as undefined in the initialize function.
Its like the async function initialize is not awaiting the async login function?
Any help is greatly appreciated, thank you again for your time.
Your login function doesn't return a promise. In fact, it doesn't return anything at all. See comments:
export const login = async address => {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ address })
};
// Note: Don't catch erros in this function, let them propagate
// so the caller knows what happened
// No need for `.then`/`.catch` in an `async` function, use `await`
// Wait for the initial resaponse
const response = await fetch(`___ENDPOINT___`, requestOptions);
const responseCode = response.status;
if (!response.ok) {
// Not an OK reseponse
if (responseCode == 400) {
// Parse the body to see if we have the message
const data = await response.json();
if (data.message === "User not yet registered") {
// Do nothing
return;
}
}
throw new Error("HTTP error " + responseCode);
}
// OK response, read the data from the body, this is also async
const data = await response.json();
return data.token;
};
Note that login will return the token or will return undefined if the response code was 400 and the body of that response was valid JSON that defined a message property with the text "User not yet registered". _initialize will need to check for that. Also, _initialize should handle any errors from login.
If anyone knows why the above code doesn't work, that would still be very useful to know, however I came up with a solution :
export const login = async address => {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ address })
};
const response = await fetch(`___ENDPOINT___`, requestOptions)
const data = await response.json();
const responseCode = response.status;
if(responseCode == 200){
console.log(data.token)
const token = data.token;
return token;
}
if(responseCode == 400){
if(data.message === "User not yet registered"){
// Do nothing
return;
}
}
}

How to resolve Empty error with status code 500 axios?

this is my code :
Express Routes:
router.route('/block')
.post(controller.ticketBlocking);
Express Controller:
const axios = require('axios');
const OAuth = require('oauth-1.0a');
const crypto = require('crypto');
const ticketBlocking = (req, res) => {
const data = JSON.stringify({
source = req.body.source
});
const oauth = OAuth({
consumer: {
key: '....', //Hided the key
secret: '....', //Hided the secret
},
signature_method: 'HMAC-SHA1',
hash_function(base_string, key) {
return crypto.createHmac('sha1', key).update(base_string).digest('base64');
}
});
const request_data = {
url: 'http://link.vvv/blockTicket',
method: 'post',
};
axios({
method: request_data.method,
url: request_data.url,
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
...oauth.oauth.toHeader(oauth.oauth.authorize(request_data)),
},
data : data
})
.then((response) => {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
if (error.response) {
console.log(error.response.data);
console.log(error.response.status);
} else if (error.request) {
console.log(error.request);
} else {
console.log('Error', error.message);
}
console.log(error.config);
});
};
the npm package which am using is - "oauth-1.0a"
The problem am facing is, when i use GET method with different end point, i get an output but when ever i use POST method am getting an empty error with status code 500
I dont know where is the mistake, am using oauth1.0a for authorization, please help !

How to resolve the converting circular structure to JSON issue in fetch

I want to get the list of the repositories by providing a username.
Below is what I have done so far.
router.get('/github/:username', (req, res) => {
try {
const url = `https://api.github.com/users/${req.params.username}/repos?per_page=5&sort=created:asc&client_id=${config.get('githubClientId')}&clientSecret=${config.get('githubSecret')}`;
const headers = {
"Content-Type": "application/x-www-form-urlencoded",
};
console.log(url);
fetch(url, {
method: 'GET',
headers: headers,
}).then(data => {
if (data.status !== 200) {
return res.status(404).send({
msg: 'No GitHub profile found'
});
} else {
return data.json();
}
}).then(result => res.json(result));
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
})
When I use the dynamically created URL in the browser, I get the response
When I pass valid user-name, I get the repositories in Postman, where I am testing the API
When I pass invalid user-name, I get the following error
(node:18684) UnhandledPromiseRejectionWarning: TypeError: Converting circular structure to JSON
at JSON.stringify (<anonymous>)
at stringify (E:\Connector\node_modules\express\lib\response.js:1123:12)
at ServerResponse.json (E:\Connector\node_modules\express\lib\response.js:260:14)
at fetch.then.then.result (E:\Connector\routes\api\profile.js:396:31)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:18684) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
Can anybody tell me how to resolve this error? I have looked at many resources but could not find any concrete.
The problem is the return res.status(404).send(…) in the first then callback. The second then callback will then try to res.json(result) that return value.
You should instead write
router.get('/github/:username', (req, res) => {
const url = `https://api.github.com/users/${req.params.username}/repos?per_page=5&sort=created:asc&client_id=${config.get('githubClientId')}&clientSecret=${config.get('githubSecret')}`;
const headers = {
"Content-Type": "application/x-www-form-urlencoded",
};
console.log(url);
fetch(url, {
method: 'GET',
headers: headers,
}).then(data => {
if (data.status !== 200) {
res.status(404).send({
msg: 'No GitHub profile found'
});
} else {
return data.json().then(result => {
res.json(result);
});
}
}).catch(err => {
console.error(err.message);
res.status(500).send('Server Error');
});
})

request(...).then is not a function error when making a POST request

I'm trying to create a firebase function that makes a HTTP POST request whenever a new document is created.
This is my code:
import * as functions from 'firebase-functions';
const admin = require('firebase-admin');
const request = require("request");
exports.sendMessage = functions.firestore.document('comms/{comms}').onCreate((snap, context) => {
const newValue = snap.data();
if (newValue) {
//const email = newValue.email;
const msg = newValue.msg;
return request({
uri: "url",
method: 'POST',
body: msg,
json: true,
resolveWithFullResponse: true
}).then((response: { statusCode: number; }) => {
if (response.statusCode >= 400) {
throw new Error(`HTTP Error: ${response.statusCode}`);
}
console.log('SUCCESS! Posted', msg);
});
}
return Promise
});
Error received:
TypeError: request(...).then is not a function
at exports.sendMessage.functions.firestore.document.onCreate (/srv/lib/index.js:25:12)
at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:127:23)
at /worker/worker.js:825:24
at
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
request supports callback interfaces natively but does not return a promise, which is what you must do within a Cloud Function.
This is explained in the official Firebase video series here: https://firebase.google.com/docs/functions/video-series/. In particular watch the three videos titled "Learn JavaScript Promises" (Parts 2 & 3 especially focus on background triggered Cloud Functions, but it really worth watching Part 1 before).
You could use request-promise (https://github.com/request/request-promise) and the rp() method which "returns a regular Promises/A+ compliant promise". You would then adapt your code as follows:
import * as functions from 'firebase-functions';
const admin = require('firebase-admin');
const rp = require('request-promise');
exports.sendMessage = functions.firestore.document('comms/{comms}').onCreate((snap, context) => {
const newValue = snap.data();
if (newValue) {
const msg = newValue.msg;
var options = {
method: 'POST',
uri: '....',
body: msg,
json: true // Automatically stringifies the body to JSON
};
return rp(options)
.then(parsedBody => {
// POST succeeded...
console.log('SUCCESS! Posted', msg);
return null;
})
.catch(err => {
// POST failed...
console.log(err);
return null;
});
} else {
return null;
}
});
request module doesn't return a Promise instead try using a callback function for response.
return request({
uri: "url",
method: 'POST',
body: msg,
json: true,
resolveWithFullResponse: true
}, function (error, response, body) {
})
As in the documentation already mention you need to pass the callback to your request
var request = require('request');
request('http://www.google.com', function (error, response, body) {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); // Print the HTML for the Google homepage.
});
If you want to chain your request you can use pipe
request
.get('url/img.png')
.on('response', function(response) {
console.log(response.statusCode) // 200
console.log(response.headers['content-type']) // 'image/png'
})
.pipe(request.put('url'))
If you want to use promise you can use request-promise
var rp = require('request-promise');
rp('http://www.google.com')
.then(function (htmlString) {
// Process html...
})
.catch(function (err) {
// Crawling failed...
});
The request module work on callbacks only, If you want to make Promisify you need to do like this
const request = require('request');
const webService = {};
webService.callApi = (url, bodyObj, method) => {
return new Promise((resolve, reject) => {
const options = {
method: method || 'POST',
url: url,
headers: {
'Content-Type': 'application/json',
},
body: bodyObj,
json: true,
};
// Error Handler
const errorMessge = { code: 500, error: 'INTERNAL_SERVER_ERROR' };
request(options, (error, response, resBody) => {
if (error) {
return reject(errorMessge);
} else if (response.statusCode !== 200) {
return reject(errorMessge);
}
return resolve(resBody);
});
});
};
module.exports = webService;

Categories