Two way SSL for FeathersJS using Socket.IO with custom authentication - node.js

I'm trying to get a FeatherJS app up and running using two way ssl for authentication over Socket.IO. I've built a simple cert strategy, verifier, etc.. to handle the task of validating that the user presented is a valid user.
However -- at this point everything I try keeps sending me in loops. I swear it doesn't appear that the authentication is running and when I do get it to finally kick out some debugging messages the peerCert is never present.
What am I doing wrong here?
index.js
const server = https.createServer({
key: fs.readFileSync('certificates/my.key'),
cert: fs.readFileSync('certificates/my-cert.pem'),
ca: fs.readFileSync('certificates/ca/my-ca.pem'),
requestCert: true,
rejectUnauthorized: true
}, app).listen(port);
app.setup(server);
Now the first time I hit the site I DO get prompted for a certificate but, I can't see any evidence that that cert gets to my authentication service.
app.js
const feathers = require('feathers');
const configuration = require('feathers-configuration');
const hooks = require('feathers-hooks');
const socketio = require('feathers-socketio');
const handler = require('feathers-errors/handler');
const notFound = require('feathers-errors/not-found');
const middleware = require('./middleware');
const services = require('./services');
const appHooks = require('./app.hooks');
const authentication = require('./authentication');
const app = feathers();
// Load app configuration
app.configure(configuration());
// Enable CORS, security, compression, favicon and body parsing
app.use(cors());
app.use(helmet());
app.use(compress());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(favicon(path.join(app.get('public'), 'favicon.ico')));
// Host the public folder
app.use('/', feathers.static(app.get('public')));
// Set up Plugins and providers
app.configure(hooks());
app.configure(socketio());
// Configure other middleware (see `middleware/index.js`)
app.configure(middleware);
app.configure(authentication);
// Set up our services (see `services/index.js`)
app.configure(services);
// Configure a middleware for 404s and the error handler
app.use(notFound());
app.use(handler());
app.hooks(appHooks);
module.exports = app;
authentication.js
const authentication = require('feathers-authentication');
const jwt = require('feathers-authentication-jwt');
const cert = require('./feathers-authentication-cert.js');
module.exports = function () {
const app = this;
const config = app.get('authentication');
// Set up authentication with the secret
app.configure(authentication(config));
app.configure(jwt());
app.configure(cert());
// The `authentication` service is used to create a JWT.
// The before `create` hook registers strategies that can be used
// to create a new valid JWT (e.g. local or oauth2)
app.service('authentication').hooks({
before: {
create: [authentication.hooks.authenticate(config.strategies)],
remove: [authentication.hooks.authenticate('jwt')]
}
});
};
feathers-authentication-cert.js
const Debug = require('debug');
const merge = require('lodash.merge');
const omit = require('lodash.omit');
const pick = require('lodash.pick');
const DefaultVerifier = require('./cert-verifier.js');
const ClientCertStrategy = require('./cert-strategy.js').Strategy;
const debug = Debug('feathers-authentication-cert');
const defaults = {
name: 'cert'
};
const KEYS = [
'passReqToCallback',
'session'
];
function init(options = {}) {
return function certAuth() {
const app = this;
const _super = app.setup;
if (!app.passport) {
throw new Error(`Can not find app.passport. Did you initialize feathers-authentication before feathers-authentication-cert?`);
}
let name = options.name || defaults.name;
let authOptions = app.get('authentication') || {};
let localOptions = authOptions[name] || {};
const localSettings = merge({}, defaults, pick(authOptions, KEYS), localOptions, omit(options, ['Verifier']));
let Verifier = DefaultVerifier;
if (options.Verifier) {
Verifier = options.Verifier;
}
app.setup = function () {
let result = _super.apply(this, arguments);
let verifier = new Verifier(app, localSettings);
if (!verifier.verify) {
throw new Error(`Your verifier must implement a 'verify' function. It should have the same signature as a local passport verify callback.`)
}
// Register 'cert' strategy with passport
debug('Registering cert authentication strategy with options:', localSettings);
app.passport.use(localSettings.name, new ClientCertStrategy(localSettings, verifier.verify.bind(verifier)));
app.passport.options(localSettings.name, localSettings);
return result;
}
};
}
// Exposed Modules
Object.assign(init, {
defaults,
Verifier: DefaultVerifier
});
module.exports = init;
cert-strategy.js
const util = require('util');
const Strategy = require('passport-strategy');
/*
* passport.js TLS client certificate strategy
*/
function ClientCertStrategy(options, verify) {
if (typeof options == 'function') {
verify = options;
options = {};
}
if (!verify) throw new Error('Client cert authentication strategy requires a verify function');
Strategy.call(this);
this.name = 'client-cert';
this._verify = verify;
this._passReqToCallback = options.passReqToCallback;
}
util.inherits(ClientCertStrategy, Strategy);
ClientCertStrategy.prototype.authenticate = function(req, options) {
var that = this;
console.log('Request', req);
// Requests must be authorized
// (i.e. the certificate must be signed by at least one trusted CA)
if(!req.socket.authorized) {
that.fail();
} else {
var clientCert = req.socket.getPeerCertificate();
// The cert must exist and be non-empty
if(!clientCert || Object.getOwnPropertyNames(clientCert).length === 0) {
that.fail();
} else {
var verified = function verified(err, user) {
if (err) { return that.error(err); }
if (!user) { return that.fail(); }
that.success(user);
};
if (this._passReqToCallback) {
this._verify(req, clientCert, verified);
} else {
this._verify(clientCert, verified);
}
}
}
};
module.exports.Strategy = ClientCertStrategy;
cert-verifier.js
class CertVerifier {
constructor (app, options = {}) {
this.app = app;
this.options = options;
this.verify = this.verify.bind(this);
}
verify(req, cert, done) {
console.log(req.socket);
done= () => {
console.log('done?');
}
var subject = cert.subject;
var msg = 'Attempting PKI authentication';
if(!subject) {
console.log(msg + ' ✘ - no subject'.red);
done(null, false);
} else if(!subject.CN) {
console.log(msg + '✘ - no client CN'.red);
done(null, false);
} else {
var cn = subject.CN;
lookupUser(cn, function(err, user) {
msg = 'Authenticating ' + cn + ' with certificate';
if(!user) {
console.log(msg + ' ✘ - no such user'.red);
done(null, false);
} else {
console.log(msg + ' - ✔'.green);
done(null, user);
}
});
}
}
}
module.exports = CertVerifier;

Related

How to generate temp RTC token from agora that work with new package 'agora-rn-uikit'?

I am using agora-rn-uikit agora-rn-uikit
And it works well with this code,
const App = () => {
const [videoCall, setVideoCall] = useState(true);
const connectionData = {
appId: '<Agora App ID>',
channel: 'test',
rtcToken: "my token from agora"
};
const rtcCallbacks = {
EndCall: () => setVideoCall(false),
};
return videoCall ? (
<AgoraUIKit connectionData={connectionData} rtcCallbacks={rtcCallbacks} />
) : (
<Text onPress={()=>setVideoCall(true)}>Start Call</Text>
);
};
with this example, I can make a video chat app, and I get the token from my agora account, I just type a channel name and get a token that works, and everything is fine.
but this is a temp token and can not depend on it in production, so I went to docs of agora and built a node js server to get me the token and the server works well and give me the token, but my problem is this token dosn't work and get a black screen on mobile app and error 110 in console.
and when I add a new key called "rtcUid" and give it a value "1" the video is working, but it works only locally not remotely, and I can't see the video of another phone,
here is my node js code, that i copied from agora docs
const express = require("express");
const { RtcTokenBuilder, RtcRole } = require("agora-access-token");
require("dotenv/config");
console.log(process.env.PORT);
const app = express();
const nocache = (_, resp, next) => {
resp.header("Cache-Control", "private, no-cache, no-store, must-revalidate");
resp.header("Expires", "-1");
resp.header("Pragma", "no-cache");
next();
};
const generateRTCToken = (req, resp) => {
resp.header("Access-Control-Allow-Origin", "*");
const channelName = req.params.channel;
if (!channelName) {
return resp.status(500).json({ error: "channel is required" });
}
let uid = req.params.uid;
if (!uid || uid === "") {
return resp.status(500).json({ error: "uid is required" });
}
// get role
let role;
if (req.params.role === "publisher") {
role = RtcRole.PUBLISHER;
} else if (req.params.role === "audience") {
role = RtcRole.SUBSCRIBER;
} else {
return resp.status(500).json({ error: "role is incorrect" });
}
let expireTime = req.query.expiry;
if (!expireTime || expireTime === "") {
expireTime = 3600;
} else {
expireTime = parseInt(expireTime, 10);
}
const currentTime = Math.floor(Date.now() / 1000);
const privilegeExpireTime = currentTime + expireTime;
let token;
if (req.params.tokentype === "userAccount") {
token = RtcTokenBuilder.buildTokenWithAccount(
process.env.APP_ID,
process.env.APP_CERTIFICATE,
channelName,
uid,
role,
privilegeExpireTime
);
} else if (req.params.tokentype === "uid") {
token = RtcTokenBuilder.buildTokenWithUid(
process.env.APP_ID,
process.env.APP_CERTIFICATE,
channelName,
uid,
role,
privilegeExpireTime
);
} else {
return resp.status(500).json({ error: "token type is invalid" });
}
return resp.json({ rtcToken: token, channel: channelName });
};
app.get("/rtc/:channel/:role/:tokentype/:uid", nocache, generateRTCToken);
app.listen(process.env.PORT, () => {
console.log(`Listening on port: ${process.env.PORT}`);
});
The token that works fine and solves my problem I get from agora when I enter to project config and select "Generate temp RTC token"
I need to know how to generate an agora rtcToken with node js that can work like that token I get from agora to use it with this new package agora-rn-uikit.

Issue leveraging keycloak-verify to instantiate keyclock object verifier

I'm leveraging this library here (https://www.npmjs.com/package/keycloak-verify)
For some reason.. requiring the class and then trying to instantiate gives me an error
TypeError: Keycloak is not a function
My code is below, I have also tried to print out the Keycloak object from the const and it gives me
{ default: [Function: Keycloak] }
Shouldn't just calling Keycloak() leverage that default function instead of giving me an error?
Thanks!
const Keycloak = require('keycloak-verify');
const run = async() => {
console.log(Keycloak)
const config = { realm: 'users', authServerUrl: 'localhost://8080' };
const keycloak = Keycloak(config);
const accessToken = '12312';
try {
const user = await keycloak.verifyOnline(accessToken);
console.log(user);
} catch(e) {
console.log(e);
}
}
run();
Looking at the compiled index.js it shows
var Keycloak = function Keycloak(config) {
return {
verifyOnline: verifyOnline(config),
verifyOffline: verifyOffline(config)
};
};
var _default = Keycloak;
exports["default"] = _default;

Unable to deploy NFT in terminal

I already deployed my smart contract to my wallet and connected it to my Alchemy account.
Here are my codings (Note that my contract address, PUBLIC_KEY, PRIVATE_KEY, API_URL and alchemy address are edited for security purposes).
mint-nft.js
require('dotenv').config();
const API_URL = process.env.API_URL;
const PUBLIC_KEY = process.env.PUBLIC_KEY;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL);
const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json");
const contractAddress = "My_Contract_Adress";
const nftContract = new web3.eth.Contract(contract.abi, contractAddress);
async function mintNFT(tokenURI) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //get latest nonce
//the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': 500000,
'data': nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI()
};
const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY);
signPromise.then((signedTx) => {
web3.eth.sendSignedTransaction(signedTx.rawTransaction, function(err, hash) {
if (!err) {
console.log("The hash of your transaction is: ", hash, "\nCheck Alchemy's Mempool to view the status of your transaction!");
} else {
console.log("Something went wrong when submitting your transaction:", err)
}
});
}).catch((err) => {
console.log(" Promise failed:", err);
});
}
mintNFT("https://gateway.pinata.cloud/ipfs/My_NFT_Picture_Hash");
alchemyContext.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeAlchemyContext = void 0;
var tslib_1 = require("tslib");
var sturdy_websocket_1 = tslib_1.__importDefault(require("sturdy-websocket"));
var websocket_1 = require("websocket");
var jsonRpc_1 = require("../util/jsonRpc");
var version_1 = require("../version");
var alchemySendHttp_1 = require("./alchemySendHttp");
var alchemySendWebSocket_1 = require("./alchemySendWebSocket");
var httpProvider_1 = require("./httpProvider");
var sendPayload_1 = require("./sendPayload");
var webSocketProvider_1 = require("./webSocketProvider");
var NODE_MAX_WS_FRAME_SIZE = 100 * 1024 * 1024; // 100 MB
function makeAlchemyContext(url, config) {
var makePayload = jsonRpc_1.makePayloadFactory();
if (/^https?:\/\//.test(url)) {
var alchemySend = alchemySendHttp_1.makeHttpSender(url);
var _a = sendPayload_1.makePayloadSender(alchemySend, config), sendPayload = _a.sendPayload, setWriteProvider = _a.setWriteProvider;
var senders = jsonRpc_1.makeSenders(sendPayload, makePayload);
var provider = httpProvider_1.makeAlchemyHttpProvider(sendPayload);
return { provider: provider, senders: senders, setWriteProvider: setWriteProvider };
}
else if (/^wss?:\/\//.test(url)) {
var protocol = isAlchemyUrl(url) ? "alchemy-web3-" + version_1.VERSION : undefined;
var ws = new sturdy_websocket_1.default(url, protocol, {
wsConstructor: getWebSocketConstructor(),
});
var alchemySend = alchemySendWebSocket_1.makeWebSocketSender(ws);
var _b = sendPayload_1.makePayloadSender(alchemySend, config), sendPayload = _b.sendPayload, setWriteProvider = _b.setWriteProvider;
var senders = jsonRpc_1.makeSenders(sendPayload, makePayload);
var provider = new webSocketProvider_1.AlchemyWebSocketProvider(ws, sendPayload, senders);
return { provider: provider, senders: senders, setWriteProvider: setWriteProvider };
}
else {
throw new Error("Alchemy URL protocol must be one of http, https, ws, or wss. Recieved: " + url);
}
}
exports.makeAlchemyContext = makeAlchemyContext;
function getWebSocketConstructor() {
return isNodeEnvironment()
? function (url, protocols) {
return new websocket_1.w3cwebsocket(url, protocols, undefined, undefined, undefined, {
maxReceivedMessageSize: NODE_MAX_WS_FRAME_SIZE,
maxReceivedFrameSize: NODE_MAX_WS_FRAME_SIZE,
});
}
: WebSocket;
}
function isNodeEnvironment() {
return (typeof process !== "undefined" &&
process != null &&
process.versions != null &&
process.versions.node != null);
}
function isAlchemyUrl(url) {
return url.indexOf("alchemyapi.io") >= 0;
}
.env
API_URL = "https://eth-rinkeby.alchemyapi.io/v2/KEY"
PRIVATE_KEY = "MY_PRIVATE_KEY"
PUBLIC_KEY = "MY_PUBLIC_KEY"
But then I was trying to deploy my NFT with metadata and nft-mint.js, I got these error.
Can anyone please tell me what was the error about?
Your issue may be with dotenv not reading in the values in your .env.
If you add console.log(API_URL), is it correct or is it undefined?
If it is undefined, I was able to resolve the issue by adding the path to my .env like so:
require('dotenv').config({path:"../.env"});
(In my case my mint-nft.js was in scripts/mint-nft.js
and .env is in the root directory.)

AWS-Serverless-Express How To Get Previous Route

I have an export button on my front-end that when clicked, sends a POST on our Express server to log the button click. This uses the route app.post(usagereport) . What I want to be able to do is capture the route the user was at when they clicked export. However, since the code to send the POST request is it's own route, it only ever returns that route's name when trying something like req.route.
I'm using API Gateway + Lambda + AWS-Serverless-Express.
I was thinking I could store something like req.session.previousRoute in req.session to capture the last route the user loaded and then return this to the access log code. However, I was unsure if this approach would work on Lambda or perhaps there is just a better way to handle it.
Here is my server.js (trimmed down)
// create the server and setup routes
const app = express();
const mysql = require("mysql");
// AWS-Serverless-Express https://github.com/awslabs/aws-serverless-express
const awsServerlessExpressMiddleware = require("aws-serverless-express/middleware");
app.use(awsServerlessExpressMiddleware.eventContext());
//Setup paths to database connection pools
const nawfprojectsDB = require("../lib/naWfProjectsDb.js");
const queries = require("./queries.js");
const accessLog = require("../lib/accessLog.js");
//Setup a timestamp for logging
const timestamp = new Date().toString();
// S3 Data Mitigation is needed when a data set exceeds 5 MB in size.
// This is a restriction of Lambda itself (they say 6 MB but want to ensure we dont ever hit the limit)
const s3DataMitigation = require("../lib/s3DataMitigation.js");
let environment = process.env.NODE_ENV;
app.get("/wg_data", (req, res, callback) => {
const dataSet = "wg_data";
nawfprojectsDB.query(queries.wg_data, (err, result) => {
if (err) {
console.log(err);
}
s3Data(dataSet, res, callback, result);
console.log(
timestamp,
"Returned " + result.length + " rows from " + dataSet
);
});
accessLog({ dataSet, req });
});
// Usage report everytime export button is clicked
app.post("/usagereport", (req) => {
const currentPath = dataSet;
const dataSet = "Data Exported: " + currentPath;
console.log(timestamp, "Data exported");
accessLog({ dataSet, req });
});
module.exports = app;
accessLog.js
let nawfprojectsDB = require("./naWfProjectsDb.js");
let queries = require("../routes/queries.js");
let environment = process.env.NODE_ENV;
//Insert data into access_logs table when usageLog is called
const accessLog = ({ dataSet, req }) => {
// We only want to log access when in beta, gamma, or prod. Not in development.
if (environment === "development") {
console.log("No access log as we are in dev");
} else {
// req.apiGateway comes from AWS-Serverless-Express - https://github.com/awslabs/aws-serverless-express
const user = req.apiGateway.event.requestContext.authorizer.principalId;
let sqlData = [dataSet, user, environment];
// Run the log_access query using the sqlData above
nawfprojectsDB.query(queries.log_access, sqlData, (err) => {
if (err) {
console.error("MySQL query error: " + err);
}
console.log("Access log added for: ", user, " at data set: ", dataSet);
});
}
};
module.exports = accessLog;
Solved my own question.
The way solved this was using express-session. I set req.session.previousRoute within each of my routes. I can then access this in my usagereport route.
const { v4: uuidv4 } = require("uuid");
const express = require("express");
const cookieParser = require("cookie-parser");
const session = require("express-session");
const randomString = uuidv4();
let sessionOptions = {
cookie: {
secret: randomString,
maxAge: 269999999999,
},
saveUninitialized: true,
resave: true,
};
// create the server and setup routes
const app = express();
// Add express-session Middleware - https://www.npmjs.com/package/express-session
app.use(cookieParser(randomString)); // Need cookieParser to properly parse our random string into the type of value expected by session
app.use(session(sessionOptions));
// AWS-Serverless-Express - https://github.com/awslabs/aws-serverless-express
const awsServerlessExpressMiddleware = require("aws-serverless-express/middleware");
app.use(awsServerlessExpressMiddleware.eventContext());
app.get("/wg_data", (req, res, callback) => {
const dataSet = "wg_data";
const action = "Accessed";
req.session.previousRoute = dataSet; // This is where we set the previousRoute in session
nawfprojectsDB.query(queries.wg_data, (err, result) => {
if (err) {
console.log(err);
}
s3Data(dataSet, res, callback, result);
console.log(
timestamp,
"Returned " + result.length + " rows from " + dataSet
);
});
accessLog({ dataSet, action, req });
});
// Usage report everytime export button is clicked
app.post("/usagereport", (req) => {
// Here we grab the previousRoute set in session to see the true place the data was exported from
const action = "Exported";
const previousRoute = req.session.previousRoute; // Now when usagereport is triggered, it knows the previous route from the session and uses that here.
const dataSet = previousRoute;
console.log(timestamp, "Data exported from ", previousRoute);
accessLog({ dataSet, action, req });
});

Node.js TypeError: Wit is not a constructor

How to solve "Wit is not a constructor" error coming from Node.js while executing code given by node-wit and wit.ai documentation.
// Setting up our bot
const wit = new Wit(WIT_TOKEN, actions);
I tried all the ways by upgrading and downgrading npm/node versions, but no luck.
Update: Please find the index.js source I used,
Do I need to change anything in this?
module.exports = {
Logger: require('./lib/logger.js').Logger,
logLevels: require('./lib/logger.js').logLevels,
Wit: require('./lib/wit.js').Wit,
}
'use strict';
var express = require('express');
var bodyParser = require('body-parser');
var request = require('request');
const Logger = require('node-wit').Logger;
const levels = require('node-wit').logLevels;
var app = express();
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.listen((process.env.PORT || 3000));
//const Wit = require('node-wit').Wit;
const WIT_TOKEN = process.env.WIT_TOKEN;
const FB_PAGE_TOKEN = process.env.FB_PAGE_TOKEN;
const Wit = require('node-wit').Wit;
// Server frontpage
app.get('/', function (req, res) {
debugger;
res.send('This is TestBot Server');
});
// Messenger API specific code
// See the Send API reference
// https://developers.facebook.com/docs/messenger-platform/send-api-reference
const fbReq = request.defaults({
uri: 'https://graph.facebook.com/me/messages',
method: 'POST',
json: true,
qs: { access_token: FB_PAGE_TOKEN },
headers: {'Content-Type': 'application/json'},
});
const fbMessage = (recipientId, msg, cb) => {
const opts = {
form: {
recipient: {
id: recipientId,
},
message: {
text: msg,
},
},
};
fbReq(opts, (err, resp, data) => {
if (cb) {
cb(err || data.error && data.error.message, data);
}
});
};
// See the Webhook reference
// https://developers.facebook.com/docs/messenger-platform/webhook-reference
const getFirstMessagingEntry = (body) => {
const val = body.object == 'page' &&
body.entry &&
Array.isArray(body.entry) &&
body.entry.length > 0 &&
body.entry[0] &&
body.entry[0].id === FB_PAGE_ID &&
body.entry[0].messaging &&
Array.isArray(body.entry[0].messaging) &&
body.entry[0].messaging.length > 0 &&
body.entry[0].messaging[0]
;
return val || null;
};
// Wit.ai bot specific code
// This will contain all user sessions.
// Each session has an entry:
// sessionId -> {fbid: facebookUserId, context: sessionState}
const sessions = {};
const findOrCreateSession = (fbid) => {
var sessionId;
// Let's see if we already have a session for the user fbid
Object.keys(sessions).forEach(k => {
if (sessions[k].fbid === fbid) {
// Yep, got it!
sessionId = k;
}
});
if (!sessionId) {
// No session found for user fbid, let's create a new one
sessionId = new Date().toISOString();
sessions[sessionId] = {fbid: fbid, context: {}};
}
return sessionId;
};
// Our bot actions
const actions = {
say(sessionId, context, message, cb) {
// Our bot has something to say!
// Let's retrieve the Facebook user whose session belongs to
const recipientId = sessions[sessionId].fbid;
if (recipientId) {
// Yay, we found our recipient!
// Let's forward our bot response to her.
fbMessage(recipientId, message, (err, data) => {
if (err) {
console.log(
'Oops! An error occurred while forwarding the response to',
recipientId,
':',
err
);
}
// Let's give the wheel back to our bot
cb();
});
} else {
console.log('Oops! Couldn\'t find user for session:', sessionId);
// Giving the wheel back to our bot
cb();
}
},
merge(sessionId, context, entities, message, cb) {
cb(context);
},
error(sessionId, context, error) {
console.log(error.message);
},
// You should implement your custom actions here
// See https://wit.ai/docs/quickstart
};
const wit = new Wit(WIT_TOKEN, actions);
// Message handler
app.post('/webhook', (req, res) => {
// Parsing the Messenger API response
// Setting up our bot
//const wit = new Wit(WIT_TOKEN, actions);
const messaging = getFirstMessagingEntry(req.body);
if (messaging && messaging.message && messaging.message.text) {
// Yay! We got a new message!
// We retrieve the Facebook user ID of the sender
const sender = messaging.sender.id;
// We retrieve the user's current session, or create one if it doesn't exist
// This is needed for our bot to figure out the conversation history
const sessionId = findOrCreateSession(sender);
// We retrieve the message content
const msg = messaging.message.text;
const atts = messaging.message.attachments;
if (atts) {
// We received an attachment
// Let's reply with an automatic message
fbMessage(
sender,
'Sorry I can only process text messages for now.'
);
} else if (msg) {
// We received a text message
// Let's forward the message to the Wit.ai Bot Engine
// This will run all actions until our bot has nothing left to do
wit.runActions(
sessionId, // the user's current session
msg, // the user's message
sessions[sessionId].context, // the user's current session state
(error, context) => {
if (error) {
console.log('Oops! Got an error from Wit:', error);
} else {
// Our bot did everything it has to do.
// Now it's waiting for further messages to proceed.
console.log('Waiting for futher messages.');
// Based on the session state, you might want to reset the session.
// This depends heavily on the business logic of your bot.
// Example:
// if (context['done']) {
// delete sessions[sessionId];
// }
// Updating the user's current session state
sessions[sessionId].context = context;
}
}
);
}
}
res.sendStatus(200);
});
There are two typical causes of your issue, either forgetting to require your module or forgetting to npm install it. Check if you:
Forgot to require('node-wit') and obtain the constructor from the returned object:
const Wit = require('node-wit').Wit
Properly required Wit but forgot to npm install node-wit
For everyone who are using messenger.js as your index.js use this:
const Wit = require('./lib/wit');
const log = require('./lib/log');
Please check your node_modules directory for node-wit package.
If node-wit is present then please require it before trying to create its instance.
const {Wit} = require('node-wit');
witHandler = new Wit({
accessToken: accessToken
});

Resources