Remove token field from bunyan log - node.js

I am using koa-bunyan-logger to log my request like this:
'use strict'
const koaBunyanLogger = require('koa-bunyan-logger');
const Koa = require('koa');
const mount = require('koa-mount')
const dbDriver = require('./database/interface')
const { userAgent } = require('koa-useragent');
const app = new Koa();
const server = app
.use(koaBunyanLogger())
.use(koaBunyanLogger.requestLogger({
updateLogFields: function (fields) {
delete fields.req.headers.authorization
}
}))
.use(mount(require('./routes/auth/jwt')))
.use(mount(require('./routes/knexplay')))
.listen(3000);
module.exports = server
What I am trying to do is to avoid logging tokens. But what it does is deleting the field from the original request object, crashing the app.
Even if I use formatRequestMessage(), it only effects msg field.
Is there a way to filter fields somehow?
Using serializer I am able to solve the problem but not sure if it is the best approach. Here is the code I used:
.use(koaBunyanLogger({
serializers: {
req: function (req) {
const { authorization, ...keepHeaders } = req.headers
return {
method: req.method,
url: req.url,
headers: keepHeaders
};
},
res: function (res) {
const { authorization, ...keepHeaders } = res.req.headers
const { req, ...keep } = res
return {
...keep,
req: {
method: res.req.method,
url: res.req.url,
headers: keepHeaders
}
};
},
}
}))

Related

Cannot send form data as 'Multipart/formdata'

Cannot send form data as 'Multipart/formdata' Content type in react-native expo app.
when we send formData object in post request in react -native app, we cant get req.body of req.files from node backend
export const saveUserAddVisitor = async data => {
try {
const apiUrl = configConstants.apiUrlWithPort;
const addVisitorData = await axios.post(
`${apiUrl}/api/v1/mobile/apartment/member`,
data,
{
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data',
},
},
);
return addVisitorData;
} catch (err) {
return err;
}
};
You can try something like this which works without Axios:
export const saveUserAddVisitor = async data => {
var data = new FormData()
data.append('foo', {
...
})
try {
const apiUrl = configConstants.apiUrlWithPort;
const addVisitorData = await fetch(`${apiUrl}/api/v1/mobile/apartment/member`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data'
},
body: data
})
return addVisitorData;
} catch {
return err;
}
}
After I gave you an example of working code for the client, but the problem may be from the server ;)
Your code with axios look like fine, just be sure to send FormData type, like RĂ©mi said here https://stackoverflow.com/a/72454168/16205278
You can construct your FormData before this function and use it directly in your current code with your axios function.
Service :
import axios from "axios";
import configConstants from "./config.js";
/**
* Current function to save User Add Visitor
* #param {*} data
*/
export const saveUserAddVisitor = async (data) => {
try {
const apiUrl = configConstants.apiUrlWithPort;
const addVisitorData = await axios.post(
`${apiUrl}/api/v1/mobile/apartment/member`,
data,
{
headers: {
Accept: 'application/json',
"Content-Type": "multipart/form-data"
}
}
);
return addVisitorData;
} catch (err) {
return err;
}
};
Use :
import {saveUserAddVisitor} from "./index"
const form = new FormData();
form.append("visitor", { firstName: "Jack", lastName: "Doe" });
saveUserAddVisitor(form);
API Express :
Apparently, express can't resolve multipart-form data unless some help, according following ressource : https://codex.so/handling-any-post-data-in-express
You have to use multer middleware :
const multer = require('multer');
app.post('/', multer().none(), function (req, res, next) {
req.body;
//... some code
});

Axios - How to get the Cookies from the response?

I need to get an authentication cookie from a website, while performing my request using Axios.
var axios = require('axios');
const authentication = async(login, password) => {
var data = `MYPAYLOAD${login}[...]${password}`;
var config = {
method: 'post',
url: 'https://mywebsite.com/login.aspx',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data : data,
};
try {
return await axios(config, {withCredentials: true});
} catch (error) {
console.log('Error: cannot authenticate...')
}
}
const init = async() => {
const login = 'LOGIN';
const password = 'PASSWORD';
const response = await authentication(login, password);
console.log(response);
}
init()
I'm receiving some Cookies that I need, but the one including the auth token is missing.
If I use exactly the same settings in Postman, I'm receiving the AUTH_TOKEN I'm looking for.
Where am I wrong?
Thanks

Axios and Oauth1.0 - 'status: 400, Bad Request'

I'm new on Nodejs and all the modules related with Node. I've been trying to use axios for send a Oauth1.0 Autorization signature, but i'm getting: response: { status: 400, statusText: 'Bad Request', ...}
import { BASE_URL } from '../../../config/config.js';
import axios from 'axios';
import status from 'http-status';
import OAuth from 'oauth-1.0a';
import { createHmac } from 'crypto';
import dotenv from 'dotenv';
dotenv.config();
const CONSUMERKEY = process.env.consumer_key;
const CONSUMERSECRET = process.env.consumer_secret;
const TOKENKEY = process.env.access_token;
const TOKENSECRET = process.env.token_secret;
export const oauth = OAuth({
consumer: {
key: CONSUMERKEY,
secret: CONSUMERSECRET,
},
signature_method: 'HMAC-SHA1',
hash_function(base_string, key) {
return createHmac('sha1', key)
.update(base_string)
.digest('base64')
},
})
export const token = {
key: TOKENKEY,
secret: TOKENSECRET,
}
const doRequest = async (query) => {
const request_data = {
url: `${BASE_URL}`,
method: 'GET',
params: { q: `${query}` },
};
const authHeader = oauth.toHeader(oauth.authorize(request_data, token));
return await axios.get(request_data.url, request_data.params, { headers: authHeader });
};
const searchU = async (term) => {
return await doRequest(`${term}`);
};
export const userS = async (req, res, next) => {
try {
const { query } = req;
const { data } = await searchU(query.q);
const string = JSON.stringify(data);
const Rs = JSON.parse(string);
const response = {
code: 1,
message: 'sucess',
response: Rs
};
res.status(status.OK).send(response);
} catch (error) {
next(error);
if (error.response){
console.log("Response: ");
console.log(error.response);
} else if(error.request){
console.log("Request: ");
console.log(error.request)
} else if(error.message){
console.log("Message: ");
console.log(error.message)
}
}
};
I've been also trying the solution given On this post: but there's no way I can make this work, no idea what i could be doing wron...
When i try the following code (see below), using Request module (which is deprecated) works well, but I really need to do it with Axios...
const request_data = {
url: `${BASE_URL}`,
method: 'GET',
params: { q: `${query}` },
};
const authHeader = oauth.toHeader(oauth.authorize(request_data, token));
request(
{
url: request_data.url,
method: request_data.method,
form: request_data.params,
headers: authHeader,
},
function(error, response, body) {
console.log(JSON.parse(body));
}
)
Any thoughts on what I'm doing wrong on this?? Thank you very much!!
Refer to the following link for the Request Config for Axios. I believe you need to have the query params after the header in the axios.get()
Axios Request Config
Try, the following and see how it goes:-
return await axios.get(request_data.url, { headers: authHeader }, request_data.params);

Write After End Error when Reading a File using FS

I built a program where a user can send request with a PDF URL then sever download it and forward into an external API endpoint. Now, the code able to download file but it hit this error when it start to read the file.
I must admit that Promises is something I hate to learn hence I used Async Function with Awaits and in other cases I used normal functions. Promises is so hard to grasp. The syntax make it hard to read.
Code is below:
const fs = require('fs');
const url = require("url");
const path = require("path");
const http = require('http')
const rp = require('request-promise');
const app = express();
const port = 8999;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/upload-invoice', (req, res) => {
var parsed = url.parse(req.body.FileURL);
var filename = (path.basename(parsed.pathname));
var downloaded_file_path = `invoices/${filename}`;
function download() {
var options = {
hostname: parsed.hostname,
port: 2799,
path: parsed.path,
method: 'GET'
}
const file = fs.createWriteStream(`invoices/${filename}`);
const make_request = http.request(options, (response) => {
response.pipe(file);
});
make_request.end();
try {
setTimeout(function () {
upload()
}, 1000);
} catch (error) {
console.log('An Error occured when uploading file,'+error);
}
}
async function upload() {
const flow = "Upload Invoice"
var file_stream = fs.createReadStream(downloaded_file_path)
var options = {
method: 'POST',
strictSSL: true,
uri: 'https://endpoint.goes.here',
formData: {
'file': file_stream
},
headers: {
'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryzte28ISYHOkrmyQT'
},
json: req.body,
resolveWithFullResponse: true
}
try {
var response = await rp(options)
res.send(response.body)
}
catch (error) {
console.log(`An error on ${flow} flow for unknown user. Here is more info about error,
${error}
`)
res.send("Error")
}
}
download()
});
app.listen(port)
Update:
formData: {
name: filename,
file: {
value: fs.createReadStream(downloaded_file_path),
options: {
filename: filename,
contentType: 'application/pdf'
}
}
}
I've tried this code too but it output same error.
It works after removing json: req.body
My fault.

Bottender - Enabling persistent_menu and get_started button on facebook

I am using NodeJS & Bottender to run my facebook messenger webhook.
So I want to have a get started button and a persistent menu. I checked & patterned my config on the examples provided on the repo like this one but I can't get it to be present on messenger yet.
here's my config.js
require('dotenv').config();
const {
FB_ACCESS_TOKEN,
FB_VERIFY_TOKEN,
APP_ID,
APP_SECRET,
} = process.env;
const { bots } = require('./global-session');
const profile = {
get_started: {
payload: '/start',
},
persistent_menu: [
{
locale: 'default',
composer_input_disabled: false,
call_to_actions: [
{
type: 'postback',
title: 'Menu',
payload: '/menu',
},
],
},
],
};
/* istanbul ignore next */
module.exports = {
messenger: {
bot: {
accessToken: FB_ACCESS_TOKEN,
appId: APP_ID,
appSecret: APP_SECRET,
mapPageToAccessToken: bots.getTokenByPageById,
profile,
},
server: {
verifyToken: FB_VERIFY_TOKEN,
path: '/messenger',
profile,
},
},
};
and here's how I use it
const { MessengerBot } = require('bottender');
const express = require('express');
const bodyParser = require('body-parser');
const { registerRoutes } = require('bottender/express');
const handler = require('./handlers');
const logger = require('./utils/logger');
const { APP_PORT, NODE_ENV } = process.env;
const server = express();
/* istanbul ignore next */
const verify = (req, res, buf) => {
req.rawBody = buf.toString();
};
server.use(bodyParser.json({ verify }));
server.use(require('morgan')('short', { stream: logger.logStream }));
const { messenger } = require('./config');
const bots = {
messenger: new MessengerBot(messenger.bot).onEvent(handler.messenger.execute),
// Define other platform bots here!
};
const initialize = async () => {
try {
registerRoutes(server, bots.messenger, messenger.server);
// Start server
server.listen(APP_PORT, () => logger.info(`ENV[${NODE_ENV}] - server is listening on port ${APP_PORT}...`));
} catch (e) {
logger.error('unable to start server!');
logger.error(e);
throw Error();
}
};
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'test') {
initialize();
}
module.exports = initialize;
Background on my facebook app
- It is already published to public and approved(at least for pages_messaging)
Anything I missed to do? Any help would be appreciated!
We leave messenger profile control to developers, because we can't assume when developers want to set, update or delete bots' messenger profile.
For example, you can have a script like this:
const { MessengerClient } = require('messaging-api-messenger');
const config = require('./bottender.config');
const client = MessengerClient.connect();
const tokens = [ /* */ ]; // get tokens from somewhere
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
client.setMessengerProfile(config.messenger.profile, {
access_token: token, // important! pass token to client api
});
}
Or you can call setMessengerProfile when you register your token to your database:
async function registerPage(page, token) {
// ....put page and token to database
await client.setMessengerProfile(config.messenger.profile, {
access_token: token, // important! pass token to client api
});
}

Resources