nodejs client can`t signup to nodejs passport based authentication service - node.js

I am trying to implement nodejs express passport axios based server-client communication. My goal push files to the server using the terminal. But first I try to implement an authentication service. I have passport auth service which works well when I signing throw postman app.
Also, I have implemented nodejs based client, which try signup to the server but without success.
error:
{ Error: Request failed with status code 400
at createError (/home/gefalko/gepick-devenv/node_modules/axios/lib/core/createError.js:16:15)
at settle (/home/gefalko/gepick-devenv/node_modules/axios/lib/core/settle.js:18:12)
at IncomingMessage.handleStreamEnd (/home/gefalko/gepick-devenv/node_modules/axios/lib/adapters/http.js:201:11)
at emitNone (events.js:111:20)
at IncomingMessage.emit (events.js:208:7)
at endReadableNT (_stream_readable.js:1064:12)
at _combinedTickCallback (internal/process/next_tick.js:139:11)
at process._tickCallback (internal/process/next_tick.js:181:9)
config:
{ adapter: [Function: httpAdapter],
transformRequest: { '0': [Function: transformRequest] },
transformResponse: { '0': [Function: transformResponse] },
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers:
{ Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'User-Agent': 'axios/0.18.0',
'Content-Length': 45 },
method: 'post',
url: 'http://localhost:3005/login',
data: '{"username":"gefalko","password":"mypass"}' },
request:
ClientRequest {
domain: null,
_events:
{ socket: [Function],
abort: [Function],
aborted: [Function],
error: [Function],
timeout: [Function],
prefinish: [Function: requestOnPrefinish] },
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
upgrading: false,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket:
Socket {
connecting: false,
_hadError: false,
_handle: [Object],
_parent: null,
_host: 'localhost',
_readableState: [Object],
readable: true,
domain: null,
_events: [Object],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 263,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Circular],
[Symbol(asyncId)]: 45,
[Symbol(bytesRead)]: 0 },
connection:
Socket {
connecting: false,
_hadError: false,
_handle: [Object],
_parent: null,
_host: 'localhost',
_readableState: [Object],
readable: true,
domain: null,
_events: [Object],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 263,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Circular],
[Symbol(asyncId)]: 45,
[Symbol(bytesRead)]: 0 },
_header: 'POST /login HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: application/x-www-form-urlencoded; charset=UTF-8\r\nUser-Agent: axios/0.18.0\r\nContent-Length: 45\r\nHost: localhost:3005\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent:
Agent {
domain: null,
_events: [Object],
_eventsCount: 1,
_maxListeners: undefined,
defaultPort: 80,
protocol: 'http:',
options: [Object],
requests: {},
sockets: [Object],
freeSockets: {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256 },
socketPath: undefined,
timeout: undefined,
method: 'POST',
path: '/login',
_ended: true,
res:
IncomingMessage {
_readableState: [Object],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 3,
_maxListeners: undefined,
socket: [Object],
connection: [Object],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Array],
trailers: {},
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 400,
statusMessage: 'Bad Request',
client: [Object],
_consuming: true,
_dumped: false,
req: [Circular],
responseUrl: 'http://localhost:3005/login',
redirects: [],
read: [Function] },
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable:
Writable {
_writableState: [Object],
writable: true,
domain: null,
_events: [Object],
_eventsCount: 2,
_maxListeners: undefined,
_options: [Object],
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 45,
_requestBodyBuffers: [],
_onNativeResponse: [Function],
_currentRequest: [Circular],
_currentUrl: 'http://localhost:3005/login' },
[Symbol(outHeadersKey)]:
{ accept: [Array],
'content-type': [Array],
'user-agent': [Array],
'content-length': [Array],
host: [Array] } },
response:
{ status: 400,
statusText: 'Bad Request',
headers:
{ 'x-powered-by': 'Express',
date: 'Wed, 20 Mar 2019 11:52:49 GMT',
connection: 'close',
'content-length': '11' },
config:
{ adapter: [Function: httpAdapter],
transformRequest: [Object],
transformResponse: [Object],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers: [Object],
method: 'post',
url: 'http://localhost:3005/login',
data: '{"username":"gefalko","password":"mypass"}' },
request:
ClientRequest {
domain: null,
_events: [Object],
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
upgrading: false,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Object],
connection: [Object],
_header: 'POST /login HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: application/x-www-form-urlencoded; charset=UTF-8\r\nUser-Agent: axios/0.18.0\r\nContent-Length: 45\r\nHost: localhost:3005\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Object],
socketPath: undefined,
timeout: undefined,
method: 'POST',
path: '/login',
_ended: true,
res: [Object],
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable: [Object],
[Symbol(outHeadersKey)]: [Object] },
data: 'Bad Request' } }
my client:
const axios = require('axios')
const prompt = require('prompt')
var prompt_attributes = [
{
name: 'username',
validator: /^[a-zA-Z\s\-]+$/,
warning: 'Username is not valid, it can only contains letters, spaces, or dashes'
},
{
name: 'password',
hidden: true
}
]
prompt.start();
prompt.get(prompt_attributes, function (err, result) {
if (err) {
console.log(err);
return 1;
}else {
console.log('Command-line received data:');
const username = result.username
const password = result.password
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
const reqBody = {
username: username,
password: password
}
axios.post('http://localhost:3005/login', reqBody, config).then(response => {
console.log(response.data);
}).catch(error => {
console.log(error);
})
}
});
also tried 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' but it not help.
server:
import * as express from 'express'
const app = express()
const passport = require('passport')
const LocalStrategy = require('passport-local').Strategy
const bcrypt = require('bcryptjs')
app.use(require('cookie-parser')())
app.use(require('body-parser').urlencoded({ extended: true }))
app.use(passport.initialize())
const port = 3005
/* DATABASE */
import { MongoClient, ObjectID } from 'mongodb'
const mongoUrl = "mongodb://localhost:27017/";
/* AUTHENTICATION */
MongoClient.connect(mongoUrl, async (err, db) => {
const myDB = db.db('mydb')
const Users = myDb.collection('users')
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
passport.use(new LocalStrategy(
function(username, password, done) {
Users.findOne({ username: username }, async function(err, user) {
console.log('USER FROM DB:', user)
console.log('PASS from client', password)
if (err) { return done(err); }
if (!user){
return done(null, false, { message: 'Incorrect username.' });
}
const res = await bcrypt.compare(password, user.password)
return res ? done(null, user) : done(null, false, { message: 'Incorrect username.' })
})
}))
app.post('/login',passport.authenticate('local'), (req, res) =>
{
res.send('logined as ' + req.user.username)
}
)
app.listen(port, () => console.log('http server listening on port '+port+'!'))
})
Also, I found that my request body is parsed strange on the server when it`s come from node client and it is parsed correctly when it's coming from postman.
request body from nodejs axios client:
{ '{"username":"gefalko","password":"mypass"}': '' }
request body from postman client:
{"username":"gefalko","password":"mypass"}

Try this format :
var qs = require('qs');
axios.post('/foo', qs.stringify({ 'bar': 123 } , config);

Related

401 Unauthorized when consuming Business Central OData Web Service in NodeJs

I have been trying to connect and retrieve a list of data from Business Central.
The Web Service has been exposed from BC and the OData link works and displays the JSON for the data in "Articles".
I'm trying to consumer that Service with NodeJS using NTLM (i also tried basic authentication and the same issue persists.
Please find below the file responsible for consuming the BC Web Service followed by the 401 error.
I have already stored all the information needed for NTLM config in my .env file, I did try to hardcode them and the same issue
const express = require("express");
const axios = require("axios");
const router = express.Router();
const ntlm = require("express-ntlm");
//const Article = require("../models/Articles.js");
const domain = process.env.DOMAIN;
const username = process.env.USERNAME;
const password = process.env.PASSWORD;
// Define auth function
const auth = ntlm({
debug: console.log,
domain,
username,
password,
ntlm_version: 2,
reconnect: true,
send_401: function (res) {
res.sendStatus(401);
},
badrequest: function (res) {
res.sendStatus(400);
},
});
// Get customer data from Business Central
async function getArticlesFromBC() {
try {
const options = {
auth: {
username,
password,
workstation: process.env.WORKSTATION,
domain,
},
};
const companyId = "CRONUS France S.A.";
const encodedCompanyId = encodeURIComponent(companyId);
const url = `http://${process.env.SERVER}:7048/BC210/ODataV4/Company('${encodedCompanyId}')/ItemListec`;
const response = await axios.get(url, options);
return response.data;
} catch (error) {
console.error(error);
throw new Error("Error retrieving articles data from Business Central");
}
}
// Route to get article data
router.get("/", auth, async (req, res) => {
try {
const user = req.ntlm;
console.log(user);
let articles = await getArticlesFromBC();
res.json(articles);
} catch (error) {
console.error(error);
res
.status(500)
.send("Error retrieving articles data from Business Central");
}
});
module.exports = router;
Explanation
This is the auth function that will use the variables set in .env to authenticate the user in order to access the BC endpoint
// Define auth function
const auth = ntlm({
debug: console.log,
domain,
username,
password,
ntlm_version: 2,
reconnect: true,
send_401: function (res) {
res.sendStatus(401);
},
badrequest: function (res) {
res.sendStatus(400);
},
});
This is the function that connects to the endpoint URL and returns the JSON file, this is where the error triggers
// Get article data from Business Central
async function getArticlesFromBC() {
try {
const options = {
auth: {
username,
password,
workstation: process.env.WORKSTATION,
domain,
},
};
const companyId = "CRONUS France S.A.";
const encodedCompanyId = encodeURIComponent(companyId);
const url = `http://${process.env.SERVER}:7048/BC210/ODataV4/Company('${encodedCompanyId}')/ItemListec`;
const response = await axios.get(url, options);
return response.data;
} catch (error) {
console.error(error);
throw new Error("Error retrieving articles data from Business Central");
}
}
This is the route to access the nodejs api
// Route to get article data
router.get("/", auth, async (req, res) => {
try {
const user = req.ntlm;
console.log(user);
let articles = await getArticlesFromBC();
res.json(articles);
} catch (error) {
console.error(error);
res
.status(500)
.send("Error retrieving articles data from Business Central");
}
});
This is the 401 error json, you can see at the beginning that NTLM authenticates successfully but then it throws the catch block in the get function
[express-ntlm] No Authorization header present
[express-ntlm] No domaincontroller was specified, all Authentication messages are valid.
{
DomainName: 'DESKTOP-1EF91E4',
UserName: 'ahmed',
Workstation: 'DESKTOP-1EF91E4',
Authenticated: true
}
AxiosError: Request failed with status code 401
at settle (D:\ESPRIT\5eme\PFE\B2B-ERP-MERN-App\node_modules\axios\dist\node\axios.cjs:1900:12)
at IncomingMessage.handleStreamEnd (D:\ESPRIT\5eme\PFE\B2B-ERP-MERN-App\node_modules\axios\dist\node\axios.cjs:2944:11)
at IncomingMessage.emit (events.js:412:35)
at endReadableNT (internal/streams/readable.js:1317:12)
at processTicksAndRejections (internal/process/task_queues.js:82:21) {
code: 'ERR_BAD_REQUEST',
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
adapter: [ 'xhr', 'http' ],
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: { FormData: [Function], Blob: null },
validateStatus: [Function: validateStatus],
headers: AxiosHeaders {
Accept: 'application/json, text/plain, */*',
'User-Agent': 'axios/1.3.3',
'Accept-Encoding': 'gzip, compress, deflate, br'
},
auth: {
username: 'ahmed',
password: undefined,
workstation: 'DESKTOP-1EF91E4',
domain: undefined
},
method: 'get',
url: "http://desktop-1ef91e4:7048/BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec",
data: undefined
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype] {
abort: [Function (anonymous)],
aborted: [Function (anonymous)],
connect: [Function (anonymous)],
error: [Function (anonymous)],
socket: [Function (anonymous)],
timeout: [Function (anonymous)],
prefinish: [Function: requestOnPrefinish]
},
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: 'desktop-1ef91e4',
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Circular *1],
[Symbol(async_id_symbol)]: 465,
[Symbol(kHandle)]: [TCP],
[Symbol(kSetNoDelay)]: false,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(RequestTimeout)]: undefined
},
_header: "GET /BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec HTTP/1.1\r\n" +
'Accept: application/json, text/plain, */*\r\n' +
'User-Agent: axios/1.3.3\r\n' +
'Accept-Encoding: gzip, compress, deflate, br\r\n' +
'Host: desktop-1ef91e4:7048\r\n' +
'Authorization: Basic YWhtZWQ6\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: noopPendingOutput],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 80,
protocol: 'http:',
options: [Object],
requests: {},
sockets: [Object],
freeSockets: {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 1,
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: "/BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec",
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 4,
_maxListeners: undefined,
socket: [Socket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Array],
trailers: {},
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 401,
statusMessage: 'Unauthorized',
client: [Socket],
_consuming: false,
_dumped: false,
req: [Circular *1],
responseUrl: "http://ahmed:#desktop-1ef91e4:7048/BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec",
redirects: [],
[Symbol(kCapture)]: false,
[Symbol(RequestTimeout)]: undefined
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'desktop-1ef91e4',
protocol: 'http:',
_redirectable: Writable {
_writableState: [WritableState],
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
_options: [Object],
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: [Circular *1],
_currentUrl: "http://ahmed:#desktop-1ef91e4:7048/BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec",
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [Array],
'user-agent': [Array],
'accept-encoding': [Array],
host: [Array],
authorization: [Array]
}
},
response: {
status: 401,
statusText: 'Unauthorized',
headers: AxiosHeaders {
'content-length': '0',
server: 'Microsoft-HTTPAPI/2.0',
'www-authenticate': 'Negotiate',
date: 'Thu, 16 Feb 2023 11:49:51 GMT',
connection: 'close'
},
config: {
transitional: [Object],
adapter: [Array],
transformRequest: [Array],
transformResponse: [Array],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: [Object],
validateStatus: [Function: validateStatus],
headers: [AxiosHeaders],
auth: [Object],
method: 'get',
url: "http://desktop-1ef91e4:7048/BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec",
data: undefined
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Socket],
_header: "GET /BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec HTTP/1.1\r\n" +
'Accept: application/json, text/plain, */*\r\n' +
'User-Agent: axios/1.3.3\r\n' +
'Accept-Encoding: gzip, compress, deflate, br\r\n' +
'Host: desktop-1ef91e4:7048\r\n' +
'Authorization: Basic YWhtZWQ6\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: "/BC210/ODataV4/Company('CRONUS%20France%20S.A.')/ItemListec",
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'desktop-1ef91e4',
protocol: 'http:',
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype]
},
data: ''
}
}
Error: Error retrieving articles data from Business Central
at getArticlesFromBC (D:\ESPRIT\5eme\PFE\B2B-ERP-MERN-App\routes\ArticleRoutes.js:44:11)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async D:\ESPRIT\5eme\PFE\B2B-ERP-MERN-App\routes\ArticleRoutes.js:53:20
Even though it works perfectly in postman (NTLM authentication on Server Instance is enabled for that to happen)
express-ntlm seemed to authenticate the user but dosent follow up for some reason.
I switched to httpntlm and it worked perfectly, here is the new code in the node API file :
const express = require("express");
const router = express.Router();
const httpntlm = require("httpntlm");
// Get article data from Business Central
async function getArticlesFromBC() {
const username = process.env.USERNAME;
const password = process.env.PASSWORD;
const domain = process.env.DOMAIN;
const workstation = process.env.WORKSTATION;
const encodedCompanyId = encodeURIComponent("CRONUS France S.A.");
const url = `http://${process.env.SERVER}:7048/BC210/ODataV4/Company('${encodedCompanyId}')/ItemListec`;
const options = {
url: url,
username: username,
password: password,
workstation: workstation,
domain: domain
};
return new Promise((resolve, reject) => {
httpntlm.get(options, (err, res) => {
if (err) {
console.error(err);
reject(err);
} else {
resolve(res.body);
}
});
});
}
// Route to get article data
router.get("/", async (req, res) => {
try {
const articles = await getArticlesFromBC();
res.json(articles);
} catch (error) {
console.error("Error retrieving articles data from Business Central:", error.message);
res.status(500).send("Error retrieving articles data from Business Central");
}
});
module.exports = router;

proxy setup with http-proxy-middleware, unexpected error occurs periodically

As a part of my React (front) / Express (backend) project, I have a web crawler that crawls buffer, decodes then obtain desired data using cheerio.
(Just to clarify, I am not using any data obtained for commercial use, but only for a personal project.)
server.js:
require("dotenv").config();
const express = require("express");
const cors = require("cors");
const http = require("http");
const axios = require("axios");
const cheerio = require("cheerio");
const app = express();
app.set("port", process.env.PORT || 3001);
app.use(cors());
app.use(
express.json({
verify: (request, _, buffer) => {
request.buffer = buffer;
},
})
);
app.get("/", (_, response) => {
response.send("index");
});
app.get("/:menu_id", async (request, response) => {
try {
const buffer = await axios.get("https://cafe.naver.com/ArticleList.nhn", {
params: {
"search.clubid": 10050146,
"search.menuid": request.params.menu_id,
"search.boardType": "L",
userDisplay: 10,
},
headers: {
"Content-Type": "application/json",
},
responseType: "arraybuffer",
});
const decoder = new TextDecoder("EUC-KR");
const content = decoder.decode(buffer.data);
const $ = cheerio.load(content);
const list = $("div.article-board:nth-of-type(4)>table>tbody>tr");
let articles = [];
list.each((_, tag) => {
const url = `https://cafe.naver.com${$(tag).find("a.article").attr("href")}`;
const number = $(tag).find("div.inner_number").text();
const title = $(tag).find("a.article").contents().last().text().trim();
const author = $(tag).find(".p-nick>a").text();
const date = $(tag)
.find("td.td_date")
.text()
.replace(/(\d{4}).(\d{2}).(\d{2})./g, "$2-$3");
articles.push({ number, title, author, date, url });
});
response.send(articles);
} catch (error) {
response.send(error);
}
});
const server = http.createServer(app);
server.listen(app.get("port"), () => {
console.log(`App is listening to port: ${app.get("port")}`);
});
Then I have setup a proxy using http-proxy-middleware on front-end.
setupProxy.js:
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = (app) => {
app.use(
createProxyMiddleware("/api", {
target: "http://localhost:3001",
changeOrigin: true,
pathRewrite: {
"^/api": "",
},
})
);
};
Articles.js:
import React, { useEffect, useState } from "react";
import axios from "axios";
import Article from "./Article";
import Loading from "./Loading";
const Articles = (props) => {
const [articles, setArticles] = useState();
useEffect(() => {
const getArticles = () => {
axios
.get(`/api/naver/cafe/${props.menu.id}`)
.then((response) => {
console.log(response);
setArticles(response.data);
})
.catch((error) => {
console.log(error);
});
};
getArticles();
}, []);
return (
<div className="articles">
<div className="menuTitle">{props.menu.title}</div>
{articles ? (
<ul className="menuContent">
{Object.entries(articles).map(([key, article]) => (
<Article article={article} key={key} />
))}
</ul>
) : (
<Loading />
)}
</div>
);
};
export default Articles;
So, this actually works and fetches data as I expected, but then when the server is left running idle, doing nothing, the client fails to fetch data from the server on page refresh (or maybe on a new session) and spits the long, long error below:
/project/node_modules/axios/dist/node/axios.cjs:725
AxiosError.call(axiosError, error.message, code, config, request, response);
^
AxiosError: read ECONNRESET
at AxiosError.from (/project/node_modules/axios/dist/node/axios.cjs:725:14)
at RedirectableRequest.handleRequestError (/project/node_modules/axios/dist/node/axios.cjs:2467:25)
at RedirectableRequest.emit (node:events:513:28)
at eventHandlers.<computed> (/project/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) {
syscall: 'read',
code: 'ECONNRESET',
errno: -104,
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
adapter: [Function: httpAdapter],
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: {
FormData: [Function: FormData] {
LINE_BREAK: '\r\n',
DEFAULT_CONTENT_TYPE: 'application/octet-stream'
},
Blob: [class Blob]
},
validateStatus: [Function: validateStatus],
headers: AxiosHeaders {
'User-Agent': 'axios/1.1.3',
'Accept-Encoding': 'gzip, deflate, br',
[Symbol(defaults)]: { Accept: 'application/json, text/plain, */*' }
},
params: {
'search.clubid': 10050146,
'search.menuid': '385',
'search.boardType': 'L',
userDisplay: 10
},
responseType: 'arraybuffer',
method: 'get',
url: 'https://cafe.naver.com/ArticleList.nhn',
data: undefined
},
request: <ref *3> Writable {
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
buffered: [],
bufferedIndex: 0,
allBuffers: true,
allNoop: true,
pendingcb: 0,
constructed: true,
prefinished: false,
errorEmitted: false,
emitClose: true,
autoDestroy: true,
errored: null,
closed: false,
closeEmitted: false,
[Symbol(kOnFinished)]: []
},
_events: [Object: null prototype] {
response: [Function: handleResponse],
error: [Function: handleRequestError],
socket: [Function: handleRequestSocket]
},
_eventsCount: 3,
_maxListeners: undefined,
_options: {
maxRedirects: 21,
maxBodyLength: Infinity,
protocol: 'https:',
path: '/ArticleList.nhn?search.clubid=10050146&search.menuid=385&search.boardType=L&userDisplay=10',
method: 'GET',
headers: [Object: null prototype] {
Accept: 'application/json, text/plain, */*',
'User-Agent': 'axios/1.1.3',
'Accept-Encoding': 'gzip, deflate, br'
},
agents: { http: undefined, https: undefined },
auth: undefined,
beforeRedirect: [Function: dispatchBeforeRedirect],
beforeRedirects: { proxy: [Function: beforeRedirect] },
hostname: 'cafe.naver.com',
port: '',
agent: undefined,
nativeProtocols: {
'http:': {
_connectionListener: [Function: connectionListener],
METHODS: [
'ACL', 'BIND', 'CHECKOUT',
'CONNECT', 'COPY', 'DELETE',
'GET', 'HEAD', 'LINK',
'LOCK', 'M-SEARCH', 'MERGE',
'MKACTIVITY', 'MKCALENDAR', 'MKCOL',
'MOVE', 'NOTIFY', 'OPTIONS',
'PATCH', 'POST', 'PROPFIND',
'PROPPATCH', 'PURGE', 'PUT',
'REBIND', 'REPORT', 'SEARCH',
'SOURCE', 'SUBSCRIBE', 'TRACE',
'UNBIND', 'UNLINK', 'UNLOCK',
'UNSUBSCRIBE'
],
STATUS_CODES: {
'100': 'Continue',
'101': 'Switching Protocols',
'102': 'Processing',
'103': 'Early Hints',
'200': 'OK',
'201': 'Created',
'202': 'Accepted',
'203': 'Non-Authoritative Information',
'204': 'No Content',
'205': 'Reset Content',
'206': 'Partial Content',
'207': 'Multi-Status',
'208': 'Already Reported',
'226': 'IM Used',
'300': 'Multiple Choices',
'301': 'Moved Permanently',
'302': 'Found',
'303': 'See Other',
'304': 'Not Modified',
'305': 'Use Proxy',
'307': 'Temporary Redirect',
'308': 'Permanent Redirect',
'400': 'Bad Request',
'401': 'Unauthorized',
'402': 'Payment Required',
'403': 'Forbidden',
'404': 'Not Found',
'405': 'Method Not Allowed',
'406': 'Not Acceptable',
'407': 'Proxy Authentication Required',
'408': 'Request Timeout',
'409': 'Conflict',
'410': 'Gone',
'411': 'Length Required',
'412': 'Precondition Failed',
'413': 'Payload Too Large',
'414': 'URI Too Long',
'415': 'Unsupported Media Type',
'416': 'Range Not Satisfiable',
'417': 'Expectation Failed',
'418': "I'm a Teapot",
'421': 'Misdirected Request',
'422': 'Unprocessable Entity',
'423': 'Locked',
'424': 'Failed Dependency',
'425': 'Too Early',
'426': 'Upgrade Required',
'428': 'Precondition Required',
'429': 'Too Many Requests',
'431': 'Request Header Fields Too Large',
'451': 'Unavailable For Legal Reasons',
'500': 'Internal Server Error',
'501': 'Not Implemented',
'502': 'Bad Gateway',
'503': 'Service Unavailable',
'504': 'Gateway Timeout',
'505': 'HTTP Version Not Supported',
'506': 'Variant Also Negotiates',
'507': 'Insufficient Storage',
'508': 'Loop Detected',
'509': 'Bandwidth Limit Exceeded',
'510': 'Not Extended',
'511': 'Network Authentication Required'
},
Agent: [Function: Agent] { defaultMaxSockets: Infinity },
ClientRequest: [Function: ClientRequest],
IncomingMessage: [Function: IncomingMessage],
OutgoingMessage: [Function: OutgoingMessage],
Server: [Function: Server],
ServerResponse: [Function: ServerResponse],
createServer: [Function: createServer],
validateHeaderName: [Function: __node_internal_],
validateHeaderValue: [Function: __node_internal_],
get: [Function: get],
request: [Function: request],
setMaxIdleHTTPParsers: [Function: setMaxIdleHTTPParsers],
maxHeaderSize: [Getter],
globalAgent: [Getter/Setter]
},
'https:': {
Agent: [Function: Agent],
globalAgent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: [Object: null prototype],
requests: [Object: null prototype] {},
sockets: [Object: null prototype],
freeSockets: [Object: null prototype] {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 1,
maxCachedSessions: 100,
_sessionCache: [Object],
[Symbol(kCapture)]: false
},
Server: [Function: Server],
createServer: [Function: createServer],
get: [Function: get],
request: [Function: request]
}
},
pathname: '/ArticleList.nhn',
search: '?search.clubid=10050146&search.menuid=385&search.boardType=L&userDisplay=10'
},
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: <ref *1> ClientRequest {
_events: [Object: null prototype] {
response: [Function: bound onceWrapper] {
listener: [Function (anonymous)]
},
abort: [Function (anonymous)],
aborted: [Function (anonymous)],
connect: [Function (anonymous)],
error: [Function (anonymous)],
socket: [Function (anonymous)],
timeout: [Function (anonymous)]
},
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
strictContentLength: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: false,
socket: <ref *2> TLSSocket {
_tlsOptions: {
allowHalfOpen: undefined,
pipe: false,
secureContext: SecureContext { context: SecureContext {} },
isServer: false,
requestCert: true,
rejectUnauthorized: true,
session: undefined,
ALPNProtocols: undefined,
requestOCSP: undefined,
enableTrace: undefined,
pskCallback: undefined,
highWaterMark: undefined,
onread: undefined,
signal: undefined
},
_secureEstablished: false,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
secureConnecting: true,
_SNICallback: null,
servername: null,
alpnProtocol: null,
authorized: false,
authorizationError: null,
encrypted: true,
_events: [Object: null prototype] {
close: [
[Function: onSocketCloseDestroySSL],
[Function],
[Function: onClose],
[Function: socketCloseListener]
],
end: [ [Function: onConnectEnd], [Function: onReadableStreamEnd] ],
newListener: [Function: keylogNewListener],
secure: [Function: onConnectSecure],
session: [Function (anonymous)],
free: [Function: onFree],
timeout: [Function: onTimeout],
agentRemove: [Function: onRemove],
error: [Function: socketErrorListener],
drain: [Function: ondrain]
},
_eventsCount: 10,
connecting: false,
_hadError: true,
_parent: null,
_host: 'cafe.naver.com',
_closeAfterHandlingError: false,
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
flowing: true,
ended: false,
endEmitted: false,
reading: true,
constructed: true,
sync: false,
needReadable: true,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
errorEmitted: true,
emitClose: false,
autoDestroy: true,
destroyed: true,
errored: Error: read ECONNRESET
at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {
errno: -104,
code: 'ECONNRESET',
syscall: 'read'
},
closed: true,
closeEmitted: true,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: false,
dataEmitted: false,
decoder: null,
encoding: null,
[Symbol(kPaused)]: false
},
_maxListeners: undefined,
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: true,
decodeStrings: false,
defaultEncoding: 'utf8',
length: 253,
writing: true,
corked: 0,
sync: false,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: [Function: bound onFinish],
writelen: 253,
afterWriteTickInfo: null,
buffered: [],
bufferedIndex: 0,
allBuffers: true,
allNoop: true,
pendingcb: 1,
constructed: true,
prefinished: false,
errorEmitted: true,
emitClose: false,
autoDestroy: true,
errored: Error: read ECONNRESET
at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {
errno: -104,
code: 'ECONNRESET',
syscall: 'read'
},
closed: true,
closeEmitted: true,
[Symbol(kOnFinished)]: []
},
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: null,
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular *1],
[Symbol(res)]: TLSWrap {
_parent: TCP {
reading: [Getter/Setter],
onconnection: null,
[Symbol(owner_symbol)]: [Circular *2],
[Symbol(handle_onclose)]: [Function: done]
},
_parentWrap: undefined,
_secureContext: SecureContext { context: SecureContext {} },
reading: true,
onkeylog: [Function: onkeylog],
onhandshakestart: {},
onhandshakedone: [Function (anonymous)],
onocspresponse: [Function: onocspresponse],
onnewsession: [Function: onnewsessionclient],
onerror: [Function: onerror],
[Symbol(owner_symbol)]: [Circular *2]
},
[Symbol(verified)]: false,
[Symbol(pendingSession)]: null,
[Symbol(async_id_symbol)]: 18,
[Symbol(kHandle)]: null,
[Symbol(lastWriteQueueSize)]: 253,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: false,
[Symbol(kSetKeepAlive)]: true,
[Symbol(kSetKeepAliveInitialDelay)]: 60,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 253,
[Symbol(connect-options)]: {
rejectUnauthorized: true,
ciphers: 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA',
checkServerIdentity: [Function: checkServerIdentity],
minDHSize: 1024,
maxRedirects: 21,
maxBodyLength: Infinity,
protocol: 'https:',
path: null,
method: 'GET',
headers: [Object: null prototype] {
Accept: 'application/json, text/plain, */*',
'User-Agent': 'axios/1.1.3',
'Accept-Encoding': 'gzip, deflate, br'
},
agents: { http: undefined, https: undefined },
auth: undefined,
beforeRedirect: [Function: dispatchBeforeRedirect],
beforeRedirects: { proxy: [Function: beforeRedirect] },
hostname: 'cafe.naver.com',
port: 443,
agent: undefined,
nativeProtocols: { 'http:': [Object], 'https:': [Object] },
pathname: '/ArticleList.nhn',
search: '?search.clubid=10050146&search.menuid=385&search.boardType=L&userDisplay=10',
_defaultAgent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: [Object: null prototype],
requests: [Object: null prototype] {},
sockets: [Object: null prototype],
freeSockets: [Object: null prototype] {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 1,
maxCachedSessions: 100,
_sessionCache: [Object],
[Symbol(kCapture)]: false
},
host: 'cafe.naver.com',
noDelay: true,
servername: 'cafe.naver.com',
_agentKey: 'cafe.naver.com:443:::::::::::::::::::::',
encoding: null,
singleUse: true
}
},
_header: 'GET /ArticleList.nhn?search.clubid=10050146&search.menuid=385&search.boardType=L&userDisplay=10 HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'User-Agent: axios/1.1.3\r\n' +
'Accept-Encoding: gzip, deflate, br\r\n' +
'Host: cafe.naver.com\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: Agent {
_events: [Object: null prototype] {
free: [Function (anonymous)],
newListener: [Function: maybeEnableKeylog]
},
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: [Object: null prototype] { noDelay: true, path: null },
requests: [Object: null prototype] {},
sockets: [Object: null prototype] {
'cafe.naver.com:443:::::::::::::::::::::': [ [TLSSocket] ]
},
freeSockets: [Object: null prototype] {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 1,
maxCachedSessions: 100,
_sessionCache: { map: {}, list: [] },
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/ArticleList.nhn?search.clubid=10050146&search.menuid=385&search.boardType=L&userDisplay=10',
_ended: false,
res: null,
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'cafe.naver.com',
protocol: 'https:',
_redirectable: [Circular *3],
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kEndCalled)]: true,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [ 'Accept', 'application/json, text/plain, */*' ],
'user-agent': [ 'User-Agent', 'axios/1.1.3' ],
'accept-encoding': [ 'Accept-Encoding', 'gzip, deflate, br' ],
host: [ 'Host', 'cafe.naver.com' ]
},
[Symbol(kUniqueHeaders)]: null
},
_currentUrl: 'https://cafe.naver.com/ArticleList.nhn?search.clubid=10050146&search.menuid=385&search.boardType=L&userDisplay=10',
[Symbol(kCapture)]: false
},
cause: Error: read ECONNRESET
at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {
errno: -104,
code: 'ECONNRESET',
syscall: 'read'
}
}
It just feels like the client loses the connection to the server via proxy.
Here's the things I've tried:
adding connection: "keep-alive" option to createProxyMiddleware header
switching http request client to built in fetch from axios
trying full length(?) url instead of shortified version (/api/naver/cafe/${props.menu.id} → http://localhost:3000/api/naver/cafe/${props.menu.id})
and nothing really resolved the issue.
The only way to fix this error is either by refreshing the page until it works or restarting the server.
I do have some Twitch's helix related apis as well, but this error only occurs with the crawling function above.
Literally spent hours and hours to troubleshoot this... and failed.
Any ideas, please?
I am using NodeJS v18.10.0 btw.

Node.js Axios POST request with application/x-www-form-urlencoded format?

I'm trying to send this POST request to IBM Cloud from Node.js using axios:
curl -k -X POST \
--header "Content-Type: application/x-www-form-urlencoded" \
--header "Accept: application/json" \
--data-urlencode "grant_type=urn:ibm:params:oauth:grant-type:apikey" \
--data-urlencode "apikey=s00pers3cret" \
"https://iam.cloud.ibm.com/identity/token"
The curl request works. I've had good luck with axios in another Node function.
The axios documentation says to do this when using application/x-www-form-urlencoded format data:
const querystring = require('querystring');
axios.post('http://something.com/', querystring.stringify({ foo: 'bar' }));
In Node I wrote:
const querystring = require('querystring');
axios.post('https://iam.cloud.ibm.com/identity/token', querystring.stringify({
grant_type: 'urn:ibm:params:oauth:grant-type:apike',
apikey: 's00pers3cret'
})
)
.then(function(response) {
console.log(response.data);
})
.catch(error => console.error(error));
Here's the full error response:
{ Error: Request failed with status code 400
at createError (/srv/node_modules/axios/lib/core/createError.js:16:15)
at settle (/srv/node_modules/axios/lib/core/settle.js:17:12)
at IncomingMessage.handleStreamEnd (/srv/node_modules/axios/lib/adapters/http.js:236:11)
at emitNone (events.js:111:20)
at IncomingMessage.emit (events.js:208:7)
at endReadableNT (_stream_readable.js:1064:12)
at _combinedTickCallback (internal/process/next_tick.js:139:11)
at process._tickDomainCallback (internal/process/next_tick.js:219:9)
config:
{ url: 'https://iam.cloud.ibm.com/identity/token',
method: 'post',
data: 'grant_type=urn%3Aibm%3Aparams%3Aoauth%3Agrant-type%3Aapike&apikey=s00pers3cret',
headers:
{ Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'axios/0.19.2',
'Content-Length': 110 },
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus] },
request:
ClientRequest {
domain:
Domain {
domain: null,
_events: [Object],
_eventsCount: 1,
_maxListeners: undefined,
members: [Array] },
_events:
{ socket: [Function],
abort: [Function],
aborted: [Function],
error: [Function],
timeout: [Function],
prefinish: [Function: requestOnPrefinish] },
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
upgrading: false,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket:
TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
_SNICallback: null,
servername: 'iam.cloud.ibm.com',
npnProtocol: false,
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object],
_eventsCount: 9,
connecting: false,
_hadError: false,
_handle: [Object],
_parent: null,
_host: 'iam.cloud.ibm.com',
_readableState: [Object],
readable: true,
domain: [Object],
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 326,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: [Object],
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular],
[Symbol(asyncId)]: 66,
[Symbol(bytesRead)]: 0 },
connection:
TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
_SNICallback: null,
servername: 'iam.cloud.ibm.com',
npnProtocol: false,
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object],
_eventsCount: 9,
connecting: false,
_hadError: false,
_handle: [Object],
_parent: null,
_host: 'iam.cloud.ibm.com',
_readableState: [Object],
readable: true,
domain: [Object],
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 326,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: [Object],
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular],
[Symbol(asyncId)]: 66,
[Symbol(bytesRead)]: 0 },
_header: 'POST /identity/token HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: axios/0.19.2\r\nContent-Length: 110\r\nHost: iam.cloud.ibm.com\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent:
Agent {
domain: null,
_events: [Object],
_eventsCount: 1,
_maxListeners: undefined,
I also tried:
const axios = require('axios');
const querystring = require('querystring');
axios({
method: 'post',
url: 'https://iam.cloud.ibm.com/identity/token',
headers: {
contentType: 'application/x-www-form-urlencoded',
accept: 'application/json'
},
data: querystring.stringify({
grant_type: 'urn:ibm:params:oauth:grant-type:apike',
apikey: 's00pers3cret'
})
})
.then(function(response) {
console.log(response.data);
})
.catch(error => console.error(error));
Same error response. Any suggestions?
There is a missing y in grant_type value, change it to this:
urn:ibm:params:oauth:grant-type:apikey
By the way, this is a server related error coming from IBM (not Axios), you can inspect them like this:
.catch(error => console.log(error.response.data));
import axios from "axios";
import queryString from "querystring";
const {data} = await axios({
url: URL,
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
},
data: queryString.stringify({
username: USERNAME,
password: PASSWORD,
}),
});
I used queryString.stringify method and got a response successful

NodeJs 403 Provided Authentication is not valid for Resource AUTH HEADER ISSUE

I am new to NodeJs and with some little NodeJs knowledge I wrote following code to basically get Access Token from Azure Tenant and later on use the same Access Token in the subsequent GET request thru AXIOS to query Azure Resource to which the Client ID Secret has been created (APP registration with Permission granted). I am able to get the Access Token but unfortunately the next GET request Fails with 403 and the same code works fine in Powershell, I am sure I am doing some small mistake in the Get Request header or some other config, Any help ? Following is the Node.Js code:-
const axios = require('axios');
const oauth = require('axios-oauth-client');
const qs = require('qs');
const APP_ID = 'XXXXXXXXXXXXXX';
const APP_SECERET = 'YYYYYYYYY';
const TOKEN_ENDPOINT ='https://login.microsoftonline.com/MyTenantIDGUID/oauth2/token?api-version=1.0';
const MS_GRAPH_SCOPE = 'Data.Read';
const resource ='https://api.loganalytics.io';
var responseval = "";
const postData = {
client_id: APP_ID,
scope: resource,
client_secret: APP_SECERET,
grant_type: 'client_credentials'
};
axios.defaults.headers.post['Content-Type'] =
'application/x-www-form-urlencoded';
axios
.post(TOKEN_ENDPOINT, qs.stringify(postData))
.then(function(response){
//console.log(response);
responseval= 'Bearer ' + response.data.access_token;
console.log ( responseval)
//Instance created below now for Log Analytics calls and passing the Access Token
var baseUrlLogAnl = 'https://api.loganalytics.io/v1/workspaces/MyLogAnalytisWorkspaceGUID/query?query=externalapistatus_CL';
var config ={headers: { 'Authorization': responseval, 'Content-Type': 'application/json' }};
axios.get(baseUrlLogAnl,config)
.then(function(response){
console.log (response)
})
.catch(function (err){
console.log(err.response);
});
})
.catch(function (err){
console.log(err.response);
});
After the execution of above code following is the RESULT and one THING STANDS OUT THAT THE AUTHORIZATION HEADER is MESSED UP with AXIO AGET and CONTENT-TYPE not sure how I can trim it:
Authorization:
'Bearer XXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKKKKKKKKKKKKKKKKKKKKKKKKSSSSSSSSSSSSSSSSSSSSSSSSZZZZZZZZZZZZZZZZZZZZwQSJ9.eyJhdWQiOiJzcG46MDAwMDAwMDItMDAwMC0wMDAwLWMwMDAtMDAwMDAwMDAwMDAwIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvZmEwNjE5ODItYTgxMC00YjQ4LTg5OGYtZGYzZTc2MWNlNzI3LyIsImlhdCI6MTU3ODc1NTg5MCwibmJmIjoxNTc4NzU1ODkwLCJleHAiOjE1Nzg3NTk3OTAsImFpbyI6IjQyTmdZQWdzMjMvdkc3dk5pNTZVVS8zMkVScFNBQT09IiwiYXBwaWQiOiIyNTMyNWY1ZC0yMWI1LTRkMmYtYWE4Ny1jMjYyNmZlZjU2OGQiLCJhcHBpZGFjciI6IjEiLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9mYTA2MTk4Mi1hODEwXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKKKKKKKKKKKKKKKKKKKKKKKKSSSSSSSSSSSSSSSSSSSSSSSSZZZZZZZZZZZZZZZZZZZZwLThmOGUtM2QyZGFhYzgzZDIzIiwidGVuYW50X3JlZ2lvbl9zY29wZSI6Ik5BIiwidGlkIjoiZmEwNjE5ODItYTgxMC00YjQ4LTg5OGYtZGYzZTc2MWNlNzI3IiwidXRpIjoiaXRxTU5FN3dRMG11aUdySDVzTlBBQSIsInZlciI6IjEuMCJ9.UoOWozRlYhrkQMzFEUGWZJxEueN_2TRlR96SmhZt5M03BRxpqzlazRXaqWAz8qnHQvSCWVdZZtV96UWU_mbMxIzoSnIBGCUvDphUMh0OIJNFTy3-xD6NgAAhhm904-7yF2mnLQu0dFq06DmTNMk0XXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKKKKKKKKKKKKKKKKKKKKKKKKSSSSSSSSSSSSSSSSSSSSSSSSZZZZZZZZZZZZZZZZZZZZhMbhZeHs7D9U0Xi1Dcw6dHbwbfgn-zThudKTXVfxFxv0KakinWdzGXkLlH_BHvAKrYw',
**'Content-Type': 'application/json',
'User-Agent': 'axios/0.19.1'** },
Here is the FULL OUTPUT:-
Bearer XXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKKKKKKKKKKKKKKKKKKKKKKKKSSSSSSSSSSSSSSSSSSSSSSSSZZZZZZZZZZZZZZZZZZZZwQSJ9.eyJhdWQiOiJzcG46MDAwMDAwMDItMDAwMC0wMDAwLWMwMDAtMDAwMDAwMDAwMDAwIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvZmEwNjE5ODItYTgxMC00YjQ4LTg5OGYtZGYzZTc2MWNlNzI3LyIsImlhdCI6MTU3ODc1NTg5MCwibmJmIjoxNTc4NzU1ODkwLCJleHAiOjE1Nzg3NTk3OTAsImFpbyI6IjQyTmdZQWdzMjMvdkc3dk5pNTZVVS8zMkVScFNBQT09IiwiYXBwaWQiOiIyNTMyNWY1ZC0yMWI1LTRkMmYtYWE4Ny1jMjYyNmZlZjU2OGQiLCJhcHBpZGFjciI6IjEiLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9mYTA2MTk4Mi1hODEwXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKKKKKKKKKKKKKKKKKKKKKKKKSSSSSSSSSSSSSSSSSSSSSSSSZZZZZZZZZZZZZZZZZZZZwLThmOGUtM2QyZGFhYzgzZDIzIiwidGVuYW50X3JlZ2lvbl9zY29wZSI6Ik5BIiwidGlkIjoiZmEwNjE5ODItYTgxMC00YjQ4LTg5OGYtZGYzZTc2MWNlNzI3IiwidXRpIjoiaXRxTU5FN3dRMG11aUdySDVzTlBBQSIsInZlciI6IjEuMCJ9.UoOWozRlYhrkQMzFEUGWZJxEueN_2TRlR96SmhZt5M03BRxpqzlazRXaqWAz8qnHQvSCWVdZZtV96UWU_mbMxIzoSnIBGCUvDphUMh0OIJNFTy3-xD6NgAAhhm904-7yF2mnLQu0dFq06DmTNMk0XXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKKKKKKKKKKKKKKKKKKKKKKKKSSSSSSSSSSSSSSSSSSSSSSSSZZZZZZZZZZZZZZZZZZZZhMbhZeHs7D9U0Xi1Dcw6dHbwbfgn-zThudKTXVfxFxv0KakinWdzGXkLlH_BHvAKrYw
{ status: 403,
statusText: 'Forbidden',
headers:
{ date: 'Sat, 11 Jan 2020 15:20:33 GMT',
'content-type': 'application/json; charset=utf-8',
'content-length': '241',
connection: 'close',
'strict-transport-security': 'max-age=15724800; includeSubDomains',
via: '1.1 draft-oms-6f46cfc587-khxdk',
'x-content-type-options': 'nosniff',
'access-control-allow-origin': '*',
'access-control-expose-headers':
'Retry-After,Age,WWW-Authenticate,x-resource-identities,x-ms-status-location',
vary: 'Accept-Encoding' },
config:
{ url:
'https://api.loganalytics.io/v1/workspaces/MYWORKSPACEGUID/query?query=externalapistatus_CL',
method: 'get',
headers:
{ Accept: 'application/json, text/plain, */*',
Authorization:
'Bearer XXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKKKKKKKKKKKKKKKKKKKKKKKKSSSSSSSSSSSSSSSSSSSSSSSSZZZZZZZZZZZZZZZZZZZZwQSJ9.eyJhdWQiOiJzcG46MDAwMDAwMDItMDAwMC0wMDAwLWMwMDAtMDAwMDAwMDAwMDAwIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvZmEwNjE5ODItYTgxMC00YjQ4LTg5OGYtZGYzZTc2MWNlNzI3LyIsImlhdCI6MTU3ODc1NTg5MCwibmJmIjoxNTc4NzU1ODkwLCJleHAiOjE1Nzg3NTk3OTAsImFpbyI6IjQyTmdZQWdzMjMvdkc3dk5pNTZVVS8zMkVScFNBQT09IiwiYXBwaWQiOiIyNTMyNWY1ZC0yMWI1LTRkMmYtYWE4Ny1jMjYyNmZlZjU2OGQiLCJhcHBpZGFjciI6IjEiLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9mYTA2MTk4Mi1hODEwXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKKKKKKKKKKKKKKKKKKKKKKKKSSSSSSSSSSSSSSSSSSSSSSSSZZZZZZZZZZZZZZZZZZZZwLThmOGUtM2QyZGFhYzgzZDIzIiwidGVuYW50X3JlZ2lvbl9zY29wZSI6Ik5BIiwidGlkIjoiZmEwNjE5ODItYTgxMC00YjQ4LTg5OGYtZGYzZTc2MWNlNzI3IiwidXRpIjoiaXRxTU5FN3dRMG11aUdySDVzTlBBQSIsInZlciI6IjEuMCJ9.UoOWozRlYhrkQMzFEUGWZJxEueN_2TRlR96SmhZt5M03BRxpqzlazRXaqWAz8qnHQvSCWVdZZtV96UWU_mbMxIzoSnIBGCUvDphUMh0OIJNFTy3-xD6NgAAhhm904-7yF2mnLQu0dFq06DmTNMk0XXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKKKKKKKKKKKKKKKKKKKKKKKKSSSSSSSSSSSSSSSSSSSSSSSSZZZZZZZZZZZZZZZZZZZZhMbhZeHs7D9U0Xi1Dcw6dHbwbfgn-zThudKTXVfxFxv0KakinWdzGXkLlH_BHvAKrYw',
'Content-Type': 'application/json',
'User-Agent': 'axios/0.19.1' },
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
data: undefined },
request:
ClientRequest {
_events:
[Object: null prototype] {
socket: [Function],
abort: [Function],
aborted: [Function],
error: [Function],
timeout: [Function],
prefinish: [Function: requestOnPrefinish] },
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket:
TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
_SNICallback: null,
servername: 'api.loganalytics.io',
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object],
_eventsCount: 8,
connecting: false,
_hadError: false,
_handle: [TLSWrap],
_parent: null,
_host: 'api.loganalytics.io',
_readableState: [ReadableState],
readable: true,
_maxListeners: undefined,
_writableState: [WritableState],
writable: false,
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: [TLSWrap],
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular],
[Symbol(res)]: [TLSWrap],
[Symbol(asyncId)]: 27,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(connect-options)]: [Object] },
connection:
TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
_SNICallback: null,
servername: 'api.loganalytics.io',
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object],
_eventsCount: 8,
connecting: false,
_hadError: false,
_handle: [TLSWrap],
_parent: null,
_host: 'api.loganalytics.io',
_readableState: [ReadableState],
readable: true,
_maxListeners: undefined,
_writableState: [WritableState],
writable: false,
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: [TLSWrap],
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular],
[Symbol(res)]: [TLSWrap],
[Symbol(asyncId)]: 27,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(connect-options)]: [Object] },
_header:
'GET /v1/workspaces/427fa6ee-41cd-4570-b201-ef6cda4bd314/query?query=externalapistatus_CL HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nAuthorization: Bearer XXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKKKKKKKKKKKKKKKKKKKKKKKKSSSSSSSSSSSSSSSSSSSSSSSSZZZZZZZZZZZZZZZZZZZZwQSJ9.eyJhdWQiOiJzcG46MDAwMDAwMDItMDAwMC0wMDAwLWMwMDAtMDAwMDAwMDAwMDAwIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvZmEwNjE5ODItYTgxMC00YjQ4LTg5OGYtZGYzZTc2MWNlNzI3LyIsImlhdCI6MTU3ODc1NTg5MCwibmJmIjoxNTc4NzU1ODkwLCJleHAiOjE1Nzg3NTk3OTAsImFpbyI6IjQyTmdZQWdzMjMvdkc3dk5pNTZVVS8zMkVScFNBQT09IiwiYXBwaWQiOiIyNTMyNWY1ZC0yMWI1LTRkMmYtYWE4Ny1jMjYyNmZlZjU2OGQiLCJhcHBpZGFjciI6IjEiLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9mYTA2MTk4Mi1hODEwXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKKKKKKKKKKKKKKKKKKKKKKKKSSSSSSSSSSSSSSSSSSSSSSSSZZZZZZZZZZZZZZZZZZZZwLThmOGUtM2QyZGFhYzgzZDIzIiwidGVuYW50X3JlZ2lvbl9zY29wZSI6Ik5BIiwidGlkIjoiZmEwNjE5ODItYTgxMC00YjQ4LTg5OGYtZGYzZTc2MWNlNzI3IiwidXRpIjoiaXRxTU5FN3dRMG11aUdySDVzTlBBQSIsInZlciI6IjEuMCJ9.UoOWozRlYhrkQMzFEUGWZJxEueN_2TRlR96SmhZt5M03BRxpqzlazRXaqWAz8qnHQvSCWVdZZtV96UWU_mbMxIzoSnIBGCUvDphUMh0OIJNFTy3-xD6NgAAhhm904-7yF2mnLQu0dFq06DmTNMk0XXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKKKKKKKKKKKKKKKKKKKKKKKKSSSSSSSSSSSSSSSSSSSSSSSSZZZZZZZZZZZZZZZZZZZZhMbhZeHs7D9U0Xi1Dcw6dHbwbfgn-zThudKTXVfxFxv0KakinWdzGXkLlH_BHvAKrYw\r\nContent-Type: application/json\r\nUser-Agent: axios/0.19.1\r\nHost: api.loganalytics.io\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent:
Agent {
_events: [Object],
_eventsCount: 1,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: [Object],
requests: {},
sockets: [Object],
freeSockets: {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
maxCachedSessions: 100,
_sessionCache: [Object] },
socketPath: undefined,
timeout: undefined,
method: 'GET',
path:
'/v1/workspaces/MYWORKSPACEGUID/query?query=externalapistatus_CL',
_ended: true,
res:
IncomingMessage {
_readableState: [ReadableState],
readable: false,
_events: [Object],
_eventsCount: 3,
_maxListeners: undefined,
socket: [TLSSocket],
connection: [TLSSocket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Array],
trailers: {},
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 403,
statusMessage: 'Forbidden',
client: [TLSSocket],
_consuming: false,
_dumped: false,
req: [Circular],
responseUrl:
'https://api.loganalytics.io/v1/workspaces/427fa6ee-41cd-4570-b201-ef6cda4bd314/query?query=externalapistatus_CL',
redirects: [] },
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable:
Writable {
_writableState: [WritableState],
writable: true,
_events: [Object],
_eventsCount: 2,
_maxListeners: undefined,
_options: [Object],
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function],
_currentRequest: [Circular],
_currentUrl:
'https://api.loganalytics.io/v1/workspaces/MyWORKSPACEGUIDhMbhZeHs7D9U0Xi1Dcw6dHbwbfgn-zThudKTXVfxFxv0KakinWdzGXkLlH_BHvAKrYw/query?query=externalapistatus_CL' },
[Symbol(isCorked)]: false,
[Symbol(outHeadersKey)]:
[Object: null prototype] {
accept: [Array],
authorization: [Array],
'content-type': [Array],
'user-agent': [Array],
host: [Array] } },
data:
{ error:
{ message: 'The provided authentication is not valid for this resource',
code: 'InvalidTokenError',
innererror: [Object] } } }
Finally I was able to resolve the issue and was on my end as the RESOURCE and the SCOPE I used in the POSTDATA was incorrect and JESONWEBTOKEN.IO was the site which helped me look at the actual token and identify the issue. Following is tokens I received from powershell and nodejs app which pointed out that aud (scope) was incorrect for nodejs:-
{
"aud": "https://api.loganalytics.io",
"iss": "https://sts.windows.net/fa061982-a810-4b48-898f-df3e761ce727/",
"iat": 1578793728,
"nbf": 1578793728,
"exp": 1578797747,
"aio": "42NgYGDRa+oxtXx52Wj9EhNf792XAA==",
"appid": "AAPP ID",
"appidacr": "1",
"idp": "https://sts.windows.net/fa061982-a810-4b48-898f-df3e761ce727/",
"oid": "07e8e976-d8ca-4090-8f8e-3d2daac83d23",
"roles": [
"Data.Read"
],
"sub": "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZzzzzz",
"tid": "XXXXXXXXXXXXXXXXXXXX",
"uti": "o95V0Bai-kSKJ2OFgL1hAA",
"ver": "1.0",
"jti": "1f30af68-8308-4a1c-ba4e-0b0cbfbc8aa6"
}
{
"aud": "spn:00000002-0000-0000-c000-000000000000",
"iss": "https://sts.windows.net/765a879e-5d61-47f9-9de8-fdca959f52b0/",
"iat": 1578792024,
"nbf": 1578792024,
"exp": 1578797718,
"aio": "42NgYHA+Pz8hku24/PsXFep8f2foAAA=",
"appid": "AAP IDD",
"appidacr": "1",
"idp": "https://sts.windows.net/765a879e-5d61-47f9-9de8-fdca959f52b0/",
"oid": "6e180301-8371-47c8-9551-a1417e67943d",
"sub": "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZzzzzzzz",
"tenant_region_scope": "NA",
"tid": "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY",
"uti": "08AhiCc4SUuJMVlxefpoAA",
"ver": "1.0",
"jti": "f158f0f6-0399-4536-896d-7e732402654e"
}
Following is the working Code:-
const axios = require('axios');
const request = require('request');
const qs = require('qs');
const APP_ID = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const APP_SECERET = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY';
const TOKEN_ENDPOINT ='https://login.microsoftonline.com/ZZZZZZZZZZZZZZZZZZZZZZ/oauth2/token?api-version=1.0';
const SCOPE = 'Data.Read';
const resource ='https://api.loganalytics.io';
var responseval = "";
var baseUrlLogAnl2 = 'https://api.loganalytics.io/v1/workspaces/logAnalyticsworkspaceID/query';
const postQuery = {
"query": "AzureActivity | summarize count() by Category"
};
const postData = {
client_id: APP_ID,
scope: 'Data.Read',
client_secret: APP_SECERET,
grant_type: 'client_credentials',
resource: resource
};
axios.defaults.headers.post['Content-Type'] =
'application/x-www-form-urlencoded';
axios
.post(TOKEN_ENDPOINT, qs.stringify(postData))
.then(function(response){
console.log(response['data']);
responseval= 'Bearer ' + response.data.access_token;
axios.defaults.headers.post['Authorization']=responseval;
//axios.defaults.headers.post['Authorization']='Bearer XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXwQSJ9.eyJhdWQiOiJodHRwczovL2FwaS5sb2dhbmFseXRpY3MuaW8iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83NjVhODc5ZS01ZDYxLTQ3ZjktOWRlOC1mZGNhOTU5ZjUyYjAvIiwiaWF0IjoxNTc4Nzk5NDYwLCJuYmYiOjE1Nzg3OTk0NjAsImV4cCI6MTU3ODgwMzM2MCwiYWlvIjoiNDJOZ1lEaXk2VnBlNmRrRXRsOE10WEVSZTE3ZUJBQT0iLCJhcHBpZCI6IjI1NjJkYWZmLTcyNGYtNDkyYi1hNWVmLWUzOGFkYTVhNTBiYiIsImFwcGlkYWNyIjoiMSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0Lzc2NWE4NzllLTVkNjEtNDdmOS05ZGU4LWZkY2E5NTlmNTJiMC8iLCJvaWQiOiI2ZTE4MDMwMS04MzcxLTQ3YzgtOTU1MS1hMTQxN2U2Nzk0M2QiLCJyb2xlcyI6WyJEYXRhLlJlYWQiXSwic3ViIjoiNmUxODAzMDEtODM3MS00N2M4LTk1NTEtYTE0MTdlNjc5NDNkIiwidGlkIjoiNzY1YTg3OWUtNWQ2MS00N2Y5LTlkZTgtZmRjYTk1OWY1MmIwIiwidXRpIjoidjVYa09qZEV0a0dXWmM1TjR5dGlBQSIsInZlciI6IjEuMCJ9.y5m2qa2LEMm_jYYoXBECkY97bFRPpiI6SIFV1MVcjy4ue-1nVPmNf2DsKkrjFLtmD4EnjlZ7RcbBqFBglb3CssJY12TPEkUk8OMchz4JCUrqQKaamMx8pBZHDPDZEOmh3wZrhnzBloT8O20ICwE9yebEy3n9G4pnIBDobImjrMmNs-B0p0wseyItMFYuEnqvM91PpuPyQl032mmXP4SzX0A7XZCHh6ITiuwfb3-p4JGbUpxAmYkCoEOYkCRTaEPiRslqQSHa8PB6pAhuMLNBifoEFRqOgDlHHqde1sdO-8d7M5VxtNoYMCwtNl6yUB2WFBxRQOy46fZq38hMC-LabQ';
axios
.post(baseUrlLogAnl2, qs.stringify(postQuery))
.then(function(response2){
console.log(response2);
})
.catch(function (err){
console.log(err.response);
});
})
.catch(function (err){
console.log(err.response);
});

request-promise cannot locate status code with resolveWithFullResponse: true

I am running express api. I am trying to make a request to an external url but i cannot seem to get the status code.
var request = require('request');
var rp = require('request-promise');
var url = require("url");
checkStatus = function(urlParam, result) {
var valid = url.parse('http://'+urlParam);
if(valid != null){
console.log('http://'+urlParam)
var _include_headers = function(body, response, resolveWithFullResponse) {
return {'headers': response.headers, 'data': body};
};
const options = {
url: `http://${urlParam}`,
followAllRedirects: true,
method: 'get',
gzip: true,
transform: _include_headers,
resolveWithFullResponse: true,
/* headers: {
'User-Agent': userAgent
},*/
};
rp(options)
.then( (response, error, data) => {
console.log(response.headers)
result(null,[{error: false, statusCode: response.statusCode, url: urlParam}]);
})
.catch(function (err) {
console.log(err)
});
}else{
result([{error:true,message: 'Invalid URI'}], null);
}
};
...
the response headers as you can see are not what i expect. if i pass and invalid url it still comes back as 200 OK
logging
http://www.doesnotexist.com
{
'cache-control': 'public, max-age=0, must-revalidate',
'content-type': 'text/html; charset=UTF-8',
date: 'Wed, 14 Aug 2019 13:49:32 GMT',
etag: '"5441e99d4c336b4a7d2d11a0f7608823-ssl-df"',
'strict-transport-security': 'max-age=31536000',
'content-encoding': 'gzip',
'content-length': '2794',
age: '133527',
connection: 'close',
server: 'Netlify',
vary: 'Accept-Encoding',
'x-nf-request-id': 'eb65c73f-aab2-4162-aba2-531393bf1a6a-455006'
}
GET /health/www.doesnotexist.com 200 416.698 ms - 46
If you try the url in the browser you will see that it should return 200 OK http://www.doesnotexist.com
https://codesandbox.io/s/newsnowapi-nodejs-g1rsn
If you browse the sandbox and try append /health/www.doesnotexist.com to url you can simulate my problem. the code is within model/healthcheck.js
This is th eoutput of resonse on my server which is not providing correct value but works fine in codesandbox
{
status: 200,
statusText: 'OK',
headers: {
date: 'Fri, 16 Aug 2019 11:28:27 GMT',
server: 'Apache',
'x-powered-by': 'PHP/7.2.1',
vary: 'Accept-Encoding,Cookie',
'cache-control': 'max-age=3, must-revalidate',
'x-xss-protection': '1; mode=block',
'x-content-type-options': 'nosniff',
'x-frame-options': 'SAMEORIGIN',
connection: 'close',
'transfer-encoding': 'chunked',
'content-type': 'text/html; charset=UTF-8'
},
config: {
url: 'http://www.doesnot.work',
method: 'get',
headers: {
Accept: 'application/json, text/plain, */*',
'User-Agent': 'axios/0.19.0'
},
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
data: undefined
},
request: ClientRequest {
_events: [Object: null prototype] {
socket: [Function],
abort: [Function],
aborted: [Function],
error: [Function],
timeout: [Function],
prefinish: [Function: requestOnPrefinish]
},
_eventsCount: 6,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: 'www.banglarelief.org',
_readableState: [ReadableState],
readable: true,
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
_writableState: [WritableState],
writable: false,
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Circular],
[Symbol(asyncId)]: 118,
[Symbol(kHandle)]: [TCP],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
connection: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: 'www.banglarelief.org',
_readableState: [ReadableState],
readable: true,
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
_writableState: [WritableState],
writable: false,
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Circular],
[Symbol(asyncId)]: 118,
[Symbol(kHandle)]: [TCP],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
_header: 'GET / HTTP/1.1\r' +
'\nAccept: application/json, text/plain, */*\r' +
'\nUser-Agent: axios/0.19.0\r' +
'\nHost: www.banglarelief.org\r' +
'\nConnection: close\r' +
'\n\r' +
'\n',
_onPendingData: [Function: noopPendingOutput],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
defaultPort: 80,
protocol: 'http:',
options: [Object],
requests: {},
sockets: [Object],
freeSockets: {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256
},
socketPath: undefined,
method: 'GET',
path: '/',
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
readable: false,
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
socket: [Socket],
connection: [Socket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Array],
trailers: {},
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 200,
statusMessage: 'OK',
client: [Socket],
_consuming: true,
_dumped: false,
req: [Circular],
responseUrl: 'http://www.banglarelief.org/',
redirects: []
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable: Writable {
_writableState: [WritableState],
writable: true,
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
_options: [Object],
_redirectCount: 1,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function],
_currentRequest: [Circular],
_currentUrl: 'http://www.banglarelief.org/',
_isRedirect: true
},
[Symbol(isCorked)]: false,
[Symbol(outHeadersKey)]: [Object: null prototype] {
accept: [Array],
'user-agent': [Array],
host: [Array]
}
},
My express sandbox https://codesandbox.io/s/newsnowapi-nodejs-g1rsn
my express returning following
HealthCheck.checkStatus = function(urlParam, result) {
var valid = url.parse("http://" + urlParam);
if (valid != null) {
console.log("http://" + urlParam);
try {
axios
.get("http://" + urlParam)
.then(response => {
if (response.isAxiosError) {
result([{ error: true, message: "Unknown error" }], null);
}
console.log("success");
result(null, [{ error: false, statusCode: 200 }]);
})
.catch(error => {
console.log("error");
result([{ error: true, message: error.message }], null);
});
} catch (error) {
console.error("getStatus" + error.message);
result(
{
error: true,
message: error.message,
statusCode: error.statusCode && error.statusCode
},
null
);
}
} else {
result([{ error: true, message: "Invalid URI" }], null);
}
};
In the sandbox it seem to be working fine but on my server it does not.

Resources