I am trying write to google sheet using 'google-spreadsheet' via Next.js API route. It works perfectly fine when I am testing locally. I can see the data being updated in the google sheet. However, when I deploy it to Vercel, it doesn't work. The 'Functions' log from Vercel shows the following error message.
Error authentication FetchError: request to https://www.googleapis.com/oauth2/v4/token failed, reason: Client network socket disconnected before secure TLS connection was established
at ClientRequest. (/var/task/node_modules/node-fetch/lib/index.js:1461:11)
at ClientRequest.emit (events.js:315:20)
at TLSSocket.socketErrorListener (_http_client.js:469:9)
at TLSSocket.emit (events.js:315:20)
at emitErrorNT (internal/streams/destroy.js:106:8)
at emitErrorCloseNT (internal/streams/destroy.js:74:3)
at processTicksAndRejections (internal/process/task_queues.js:80:21) {
type: 'system',
errno: 'ECONNRESET',
code: 'ECONNRESET',
config: {
method: 'POST',
url: 'https://www.googleapis.com/oauth2/v4/token',
data: {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: ....
Below is my code if that's any help.
export default async function addRowAPI(req, res) {
if (req.method === 'POST') {
try {
let doc;
try {
doc = new GoogleSpreadsheet(process.env.SPREADSHEET_ID);
} catch (error) {
console.log('error at line 15:', error);
}
try {
await doc.useServiceAccountAuth({
client_email: process.env.GOOGLE_SHEETS_CLIENT_EMAIL,
private_key: (process.env.GOOGLE_SHEETS_PRIVATE_KEY || '').replace(
/\\n/g,
'\n'
),
});
} catch (error) {
console.log('error authentication', error);
}
await doc.loadInfo();
console.log(doc.title);
const sheet = doc.sheetsByTitle['Test_Sheet'];
console.log(sheet.title);
console.log('addRow Doc:', doc);
const newRow = await sheet.addRow(req.body);
res.status(201).send();
} catch (error) {
res.status(500).json(error);
}
} else if (req.method === 'GET') {
res.status(200).json({ ping: 'pong' });
}
}
As mentioned in the comments, the error complains about an authentication issue which indicates wrong/non-existing credentials. Double-check you have all the environment variables properly set in Vercel.
Related
I am trying to build a project in which I will fetch the user's step count by using the google fit Rest API. For this, I have created a project on google's developer console and specified a redirection url there. Have a look to the code snippet below :
exports.getUrl = async (req, res, next) => {
try {
const oauth2Client = new google.auth.OAuth2(
process.env.GOOGLE_FIT_CLIENT_ID,
process.env.GOOGLE_FIT_CLIENT_SECRET,
process.env.GOOGLE_FIT_REDIRECTION_URL
);
console.log("oauth2Client", oauth2Client)
// generate a url that asks permissions for fitness activity scopes
const scopes = ["https://www.googleapis.com/auth/fitness.activity.read profile email openid"];
const url = oauth2Client.generateAuthUrl({
access_type: "offline",
scope: scopes,
include_granted_scopes: true,
state: JSON.stringify({
// callbackurl: req.body.callbackurl,
})
});
console.log("url", url);
var options = {
url: url,
proxy: 'http://proxy-url:port'
}
request(options, (err, response, body) => {
if (response) {
console.log("statuscode ", response && response.statusCode);
//res.status(200).send(url);
res.redirect(url);
} else {
console.log("err", err)
res.status(500).send(err);
}
});
} catch (err) {
console.log("err", err)
next(err);
}
}
exports.getSteps = async (req, res, next) => {
try {
const queryUrl = new urlParse(req.url);
const code = queryParse.parse(queryUrl.query).code;
const oauth2Client = new google.auth.OAuth2(
process.env.GOOGLE_FIT_CLIENT_ID,
process.env.GOOGLE_FIT_CLIENT_SECRET,
process.env.GOOGLE_FIT_REDIRECTION_URL
);
const token = await oauth2Client.getToken(code);
oauth2Client.setCredentials(token);
const result = await axios({
proxy: {
protocol: 'http',
host: 'proxy-url',
port: port
},
method: "POST",
headers: {
authorization: "Bearer " + token.tokens.access_token
},
"Content-Type": "application/json",
url: "https://www.googleapis.com/fitness/v1/users/me/dataset:aggregate",
data: {
"aggregateBy": [{
"dataTypeName": "com.google.step_count.delta",
"dataSourceId": "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps"
}],
"bucketByTime": { "durationMillis": 86400000 }, // This is 24 hours
"startTimeMillis": startTime, // This startTime and endTime I am getting from DB
"endTimeMillis": endTime
}
});
if (result) {
const response = [];
let stepArray = result?.data?.bucket;
for (const dataSet of stepArray) {
for (const points of dataSet.dataset) {
for (const steps of points.point) {
response.push(steps?.value[0]?.intVal);
}
}
}
res.status(200).send(response);
} else {
throw new Error('Data fetching failed!');
}
} catch (err) {
next(err);
}
}
The steps url is what I have mentioned as a redirection url on the google's developer console. I have used proxy because the urls which are getting called are not whitelisted on the server on which I am deploying the code.
Now, everything worked perfectly fine in localhost but on server, I am getting below error :
FetchError: request to https://oauth2.googleapis.com/token failed, reason: connect ECONNREFUSED 172.217.167.234:443
at ClientRequest.<anonymous> (/home/node/app/node_modules/node-fetch/lib/index.js:1491:11)
at ClientRequest.emit (node:events:394:28)
at TLSSocket.socketErrorListener (node:_http_client:447:9)
at TLSSocket.emit (node:events:394:28)
at emitErrorNT (node:internal/streams/destroy:157:8)
at emitErrorCloseNT (node:internal/streams/destroy:122:3)
at processTicksAndRejections (node:internal/process/task_queues:83:21)
This error in coming in the steps api call. I have tried to set the proxy options like below but still same error.
google.options({
proxy: 'http://proxy-url:port'
})
I have tried to use the https-proxy-agent and http-proxy-agent too but no outcome.
Also, one thing to note here is that the above error is coming when I am trying to get the data from chrome with --disable-web-security flag otherwise the first the route which is url itself is not working and throwing timeout error.
Please let me know if I am doing something wrong. Any response would be highly appreciated.
I want to get an object when the HTTP request failed.
something like this
Object = {status: 404, reason: 'Not found', body: '404 Not found'}
I have read How can I get the status code from an HTTP error in Axios?,But It does not work for me.
This is the JS code,
const axios = require('axios');
axios({
url:'https://www.icofont.cn//sd',
method:'GET',
})
.then(response=>{
console.log(response.status);
})
.catch(error=>{
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
})
I always get an undefined of the error.response.So I get an error. I can get an error object. How can I extract 404 Not found from this object?
The problem is that you are assuming the error is returned in the response. It could also be in the request (e.g. while connecting to the server before making the request). I addded console.log(error) and found that on my computer the error is in the request:
AxiosError: connect ECONNREFUSED 38.238.92.52:443
at AxiosError.from (/run/user/1000/test123/node_modules/axios/dist/node/axios.cjs:725:14)
at RedirectableRequest.handleRequestError (/run/user/1000/test123/node_modules/axios/dist/node/axios.cjs:2467:25)
at RedirectableRequest.emit (node:events:513:28)
at eventHandlers.<computed> (/run/user/1000/test123/node_modules/follow-redirects/index.js:14:24)
at ClientRequest.emit (node:events:513:28)
at TLSSocket.socketErrorListener (node:_http_client:494:9)
at TLSSocket.emit (node:events:513:28)
at emitErrorNT (node:internal/streams/destroy:151:8)
at emitErrorCloseNT (node:internal/streams/destroy:116:3)
at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
port: 443,
address: '38.238.92.52',
syscall: 'connect',
code: 'ECONNREFUSED',
errno: -111,
config: {
[...]
},
request: <ref *1> Writable {
[...]
},
cause: Error: connect ECONNREFUSED 38.238.92.52:443
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1300:16) {
errno: -111,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '38.238.92.52',
port: 443
}
}
/run/user/1000/test123/test.js:11
console.log(error.response.data);
^
TypeError: Cannot read properties of undefined (reading 'data')
at /run/user/1000/test123/test.js:11:32
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Node.js v18.10.0
The server you are connecting to does not seem to support HTTPS. It can only be connected to if https://www.icofont.cn//sd is changed to http://www.icofont.cn//sd, and then the 404 comes out.
Instead of assuming the error is in the response or request, it should be checked for. The official example explains this.
Your fixed example will look like the following:
const axios = require('axios');
axios({
url:'http://www.icofont.cn//sd',
method:'GET',
})
.then(response=>{
console.log(response.status);
})
.catch(error=>{
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
console.log(error.config);
})
I have a react native app and a node js (v16.13.2) backend.
I am storing a refresh token in the react native async storage and for every backend call I am sending this refresh token in order grant access or not. This is done in my isAuth middleware in node js.
In summary isAuth function (authService.js file) takes a refresh_token from the react native app, get a new access_token (id_token) with this refresh_token and then I verify this token with the google-auth-library.
If payload.email_verified I call next() in the authController.js file.
The following try / catch clause is in my isAuth function:
try {
console.log('with refresh token a new access token is retrieved, in order to check if email_verified');
const OAuth2 = google.auth.OAuth2;
const oauth2Client = new OAuth2(
config.CLIENT_ID_GOOGLE, // ClientID
config.CLIENT_SECRET_GOOGLE, // Client Secret
"https://www.sagly.at" // Redirect URL
);
oauth2Client.setCredentials({
refresh_token:
token
});
const res = new Promise((resolve, reject) => {
oauth2Client.getAccessToken((err, token, res) => {
if (err) {
console.log('oauth2Client.getAccessToken get access token error', err); // Handling the errors
} else {
console.log('token, res', token, res);
resolve(res);
}
});
});
const idToken = await res;
console.log('isAuth google idToken const of await res.data.id_token', idToken);
const client = new OAuth2Client(config.CLIENT_ID_GOOGLE);
// https://developers.google.com/identity/sign-in/ios/backend-auth#verify-the-integrity-of-the-id-token
result = await client.verifyIdToken({
idToken: idToken.data.id_token,
audience: config.CLIENT_ID_GOOGLE,
});
console.log('isAuth google result const of await client.verifyIdToken({}) response', result);
const payload = result.getPayload();
console.log('isAuth google payload const of result.getPayload() response', payload);
if (payload && payload.email_verified) {
console.log('next called');
return 'next';
}
} catch (err) {
console.log(err);
}
I get the following error in my catch clause:
isAuth errorMessage FetchError: Failed to retrieve verification certificates: request to https://www.googleapis.com/oauth2/v1/certs failed, reason: connect ETIMEDOUT 74.125.193.95:443
at ClientRequest.<anonymous> (/app/node_modules/node-fetch/lib/index.js:1483:11)
at ClientRequest.emit (events.js:400:28)
at ClientRequest.emit (domain.js:475:12)
at TLSSocket.socketErrorListener (_http_client.js:475:9)
at TLSSocket.emit (events.js:400:28)
at TLSSocket.emit (domain.js:475:12)
at emitErrorNT (internal/streams/destroy.js:106:8)
at emitErrorCloseNT (internal/streams/destroy.js:74:3)
at processTicksAndRejections (internal/process/task_queues.js:82:21) {
type: 'system',
errno: 'ETIMEDOUT',
code: 'ETIMEDOUT',
config: {
url: 'https://www.googleapis.com/oauth2/v1/certs',
headers: {
'User-Agent': 'google-api-nodejs-client/7.10.1',
'x-goog-api-client': 'gl-node/14.19.0 auth/7.10.1',
Accept: 'application/json'
},
paramsSerializer: [Function: paramsSerializer],
validateStatus: [Function: validateStatus],
responseType: 'json',
method: 'GET'
}
}
Can somebody help me what the reason could be for this error? If you need more specific logging or more details I am happy to add those.
Thanks for the efforts.
Those two packages are imported and in use:
const {OAuth2Client} = require("google-auth-library");
const {google} = require("googleapis");
I have a simple .NET Core WebAPI with no authentication. I added Cors with default policy. I have no problem connecting and fetching data from my React website or Postman (everything runs locally on my machine). Now I'm trying to fetch data from that API in super simple node application and I'm getting this error:
file:///Users/aw/Projects/TestNodeApp/node_modules/node-fetch/src/index.js:94
reject(new FetchError(`request to ${request.url} failed, reason: ${error.message}`, 'system', error));
^
FetchError: request to https://localhost:5001/api/teams failed, reason: self signed certificate
at ClientRequest.<anonymous> (file:///Users/aw/Projects/TestNodeApp/node_modules/node-fetch/src/index.js:94:11)
at ClientRequest.emit (node:events:394:28)
at TLSSocket.socketErrorListener (node:_http_client:447:9)
at TLSSocket.emit (node:events:394:28)
at emitErrorNT (node:internal/streams/destroy:157:8)
at emitErrorCloseNT (node:internal/streams/destroy:122:3)
at processTicksAndRejections (node:internal/process/task_queues:83:21) {
type: 'system',
errno: 'DEPTH_ZERO_SELF_SIGNED_CERT',
code: 'DEPTH_ZERO_SELF_SIGNED_CERT',
erroredSysCall: undefined
}
This is my whole node application:
import fetch from 'node-fetch';
async function fetchTeams() {
const response = await fetch('https://localhost:5001/api/teams', { method: 'GET' });
const data = await response.json();
return data;
}
(async () => {
console.log('Process started');
const teams = await fetchTeams();
console.log(teams);
})().finally(() => {
console.log('Process finished');
});
What does it mean? What Am I missing?
Btw. It works fine, when I'm fetching Github API, like this:
async function fetchGithub() {
const response = await fetch('https://api.github.com/users/Microsoft');
const data = await response.json();
return data;
}
So I assume, something is missing in the API. Something that my React website doesn't need, that node app needs.
Thanks for help!
You can use this command to set the NODE_TLS_REJECT_UNAUTHORIZED environment variable:
export NODE_TLS_REJECT_UNAUTHORIZED=0
Try trusting the self signed certificate with dotnet dev-certs
dotnet dev-certs https --trust
For more details please visit this documentation page.
When running purely under node everything works perfectly, as soon as I try to run under IIS Node, the Node http request fails every time with ENOTFOUND on getaddrinfo. This is part of a mid sized website with everything else working under Node and IIS Node, just this request to an Azure CORS web service is failing. I also have similar web service requests to google maps that work perfectly in both environments. Both the google maps and the Azure web service calls use the same shared code to handle the request.
// Simplified
//==================================
var http = require("http")
var options = {
host: "xxx.azurewebsites.net",
path: "/address?postcode=SL6+1PB",
headers: {
"SerialNo": "xxx",
"RegKey": "xxx",
"Company": "xxx"
}
};
req = https.request(options, callback);
req.end();
// The Whole thing
//===========================
function getData(host, port, path, headers, data, secure) {
return new promise(function(resolve, reject) {
var options = {}, req;
try {
options.host = host;
if(!!port) {options.port = port;}
if(!!data) {options.method = "POST";}
if(!!path) {options.path = path.replace(/\s+/g, "+");}
if(!!headers) {options.headers = headers;}
if(!!secure) {req = https.request(options, callback);}
else {req = http.request(options, callback);}
req.on("error", function(error) {
rejectPromise(reject, error, "datahttp.getData");
});
if(!!data) {req.write((typeof(data) === "object" ? JSON.stringify(data) : data));}
req.end();
}
catch(error) {
// This error object drops the message property on stringify
error = {method: "datahttp.getData", success: false, message: error.message};
rejectPromise(reject, error, "datahttp.getData");
}
function callback(response) {
var str = "";
//another chunk of data has been received, so append it to `str`
response.on("data", function(chunk) {
str += String(chunk);
});
// the whole response has been received
response.on("end", function () {
if(response.statusCode == 200) {
try {
switch(response.headers["content-type"].split(";")[0]) {
case "application/xml":
xmlToJSN(str)
.then(function(result) {
resolve({method: "datahttp.getData", success: true, data: result});
})
.catch(function(error) {
rejectPromise(reject, error, "datahttp.getData");
});
break;
case "application/json":
resolve({method: "datahttp.getData", success: true, data: JSON.parse(str)});
break;
default:
resolve({method: "datahttp.getData", success: true, data: str});
}
}
catch(error) {
rejectPromise(reject, error, "datahttp.getData");
}
}
else {
rejectPromise(reject, {message: response.statusMessage + " (" + response.statusCode + ")"}, "datahttp.getData");
}
});
response.on("error", function(error) {
rejectPromise(reject, error, "datahttp.getData");
});
}
});
}
I have been struggling with this for a few days and clearly just not seeing something very obvious, some help and a smack upside the head would be welcome
Stack Trace
====================
Trace: { [Error: getaddrinfo ENOTFOUND daaddressing-prod-ne.azurewebsites.net/ daaddressing-prod-ne.azurewebsites.net/:80]
code: 'ENOTFOUND',
errno: 'ENOTFOUND',
syscall: 'getaddrinfo',
hostname: 'daaddressing-prod-ne.azurewebsites.net/',
host: 'daaddressing-prod-ne.azurewebsites.net/',
port: 80 }
at ClientRequest.<anonymous> (C:\Dev\Node\acinet_flint\server\datahttp.js:47:29)
at emitOne (events.js:77:13)
at ClientRequest.emit (events.js:169:7)
at Socket.socketErrorListener (_http_client.js:269:9)
at emitOne (events.js:77:13)
at Socket.emit (events.js:169:7)
at connectErrorNT (net.js:1012:8)
at nextTickCallbackWith2Args (node.js:442:9)
at process._tickCallback (node.js:356:17)
{ [Error: getaddrinfo ENOTFOUND daaddressing-prod-ne.azurewebsites.net/ daaddressing-prod-ne.azurewebsites.net/:80]
code: 'ENOTFOUND',
errno: 'ENOTFOUND',
syscall: 'getaddrinfo',
hostname: 'daaddressing-prod-ne.azurewebsites.net/',
host: 'daaddressing-prod-ne.azurewebsites.net/',
port: 80,
service: 'acinet',
success: false,
method: 'datahttp.getData' }