issue while authentication and registration node-xmpp - node.js

I am trying to register a new user through node-xmpp from node.js to an ejabberd but the authentication and registration events are not invoking neither it is not connecting.
var clientXMPP = require('node-xmpp-client');
var serverXMPP = require('node-xmpp-server');
var c2s = new serverXMPP.C2SServer({
jid: 'testuser#192.168.1.1',
password: 'test'
});
c2s.on("connect", function(client) {
console.log(client)
c2s.on('register', function(opts, cb) {
console.log('REGISTER')
cb(false)
})
c2s.on("authenticate", function(opts, cb) {
console.log("AUTH" + opts.jid + " -> " +opts.password);
cb(false);
});
c2s.on('disconnect', function () {
console.log('DISCONNECT')
})
});
How can I register a new user and authenticate them in node-xmpp.

If you just need to create new user from nodejs to ejabberd server please make sure you have in-band registration enabled in ejabberd.
you don't need node-xmpp-server, you can only us node-xmpp-client. Client library will help you to connect with ejabberd admin user.
here is an example
const Client = require('node-xmpp-client');
let admin = new Client({
jid: '{adminJID}',
password: '{adminPassword}'
});
// check for errors to connect with socket
admin.connection.socket.on('error', function (error) {
// handle error
});
admin.on('online', function (data) {
// Register stanza
let stanza = new Client.Stanza('iq', {type: 'set', id: 'reg1', to: '192.168.1.1'})
.c('query', {xmlns: 'jabber:iq:register'})
.c('username').t('{username}').up() // Give a new username
.c('password').t('{password}') // Give a new user's password
admin.send(stanza); // send a stanza
});
// response stanzas
admin.on('stanza', function (stanza) {
// check for error/success
});

Related

How to use authenticate web sockets using node, ws and passport with JWT?

I have a web server written in node/express which uses passport to authenticate users with JSON Web Tokens (JWT).
For regular HTTP methods this is what I use:
app.get('/api/stuff', isLoggedIn(), (req, res) => {
//get stuff
res.send( whatever );
});
Where isLoggedIn is this function:
function isLoggedIn() {
return passport.authenticate('local-jwt', { session: false });
}
The authentication itself is handled by the 'local-jwt' configuration in passport using a JWTStrategy object and it works as expected.
Now I need to add web sockets to this app. I'm using the ws library. Here is what I have so far:
const wss = new WebSocket.Server({
port: 8080,
verifyClient: async (info, done) => {
// ???
done( result );
}
});
wss.on('connection', ws => {
// web socket events
});
How do I use the passport authentication to only allow clients with the correct token to connect to the web socket server?
You can sign the token with the user's id or anything you are using to differentiate users
var today = new Date();
var exp = new Date(today);
exp.setDate(today.getDate() + 60);
jwt.sign({
id: user._id,
exp: parseInt(exp.getTime() / 1000),
}, secret);
then pass the token to the frontend and use it to initialize your Websocket connection with it appended to the url then take and decode it in the server like this
const wss = new WebSocket.Server({
verifyClient: async (info, done) => {
console.log(info.req.url);
console.log('------verify client------');
const token = info.req.url.split('/')[1];
var decoded = jwt.verify(token, secret);
info.req.user = await User.findById(decoded.id).exec();
/*info.req.user is either null or the user and you can destroy the connection
if its null */
done(info.req);
},
server
});
This is one of many ways. There is a way with sessions but I see you don't want to use them. Also I don't know if you use mongo but it's the same for any database you just have to change some code.

Discord bot not starting up without error message

When i try to start up my bot via node the program quits again without any errors
var Discord = require('discord.io');
var bot = new Discord.Client({
token: "nope"
});
bot.connect();
console.log("hi");
bot.on('ready', function (evt) {
console.log('Connected');
});
bot.on('message', function (user, userID, channelID, message, event) {
bot.sendMessage({
to:channelID,
message:"user, userID, channelID, message, evt:"+user + userID + channelID+ message+ event
});
});
My issue was the token which was somehow breaking stuff.
My workaround, was by just coping my token from the ./auth.json and place in it manually inside the client variable. (which was retriving from the file)
before:
var client = new Discord.Client({
token: auth.token,
autorun: true
});
after:
var client = new Discord.Client({
token: "REPLACE_THIS_WITH_YOUR_TOKEN",
autorun: true
});

If MFA enabled in AWS cognito, do I need to create js on client side to call cognitoUser.authenticateUser() because of the promt for code?

I am using reactjs and node for server side.
As you can see in the "mfa required" part of the code below, if this is all on node, then I can't really do "prompt" the user for the code, I have to pass this back to the front end.
Tried solution: If I do pass the MFA required to front end and get the users input then send it back to node to call "respondToAuth" I am getting two MFA codes in my SMS message.
Have I tried other solutions?
I am hesitant to use amplify because everything is on the front end, I would ideally like to do my authentication on the back end (thus node).
Another option I am leaning towards is just using initiateAuth api instead of "cognitoUser.AuthenticateUser". This way I can get the challenge response and pass it on in sequence. But as per my initial question, I am wondering if I can implement the below code and be able to route users to input MFA code (without duplicating MFA sms message)
AWS.config.update({
region: process.env.Region
});
var AmazonCognitoIdentity = require('amazon-cognito-identity-js');
const poolData = { //--Moved to env variables
UserPoolId: process.env.UserPoolId, // your user pool id here
ClientId: process.env.ClientId // your app client id here
};
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
router.post('/api/authenticateuser', (req, res) => {
const val = req.body;
var userData = {
Username: val.value.user, // your username here
Pool: userPool
};
var authenticationData = {
Username: val.value.user, // your username here
Password: val.value.pass, // your password here
};
const authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function(result) {
console.log('You are now logged in.');
console.log(result);
const accessToken = result.getAccessToken().getJwtToken();
const idToken = result.getIdToken().getJwtToken();
res.json({
accessToken,
idToken
});
},
onFailure: function(err) {
res.json(err);
},
mfaRequired: function(codeDeliveryDetails) {
// console.log("mfa enabled");
// var verificationCode = prompt('Please input verification code' ,'');
// cognitoUser.sendMFACode(verificationCode, this);
// res.json({ MFA:codeDeliveryDetails})
}
});
})

Authentication Error when Retrieving and Editing Device Configuration on IoT-Core

I'm trying to use a backend nodeJS server to access (and edit) the device configuration on IoT-Core referring to this API docs
However, I keep getting error:
code 401 with error message "message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED".
I created a service account and a key from Google IAM, and gave it Cloud IoT Device Controller permissions, which could update device configurations but not create or delete. Subsequently, I changed it to Cloud IoT Admin and even Project Editor permissions, but still saw the same error message. Am I getting the keys all wrong, or not doing something else I should be doing?
Code below was how I invoked the request
function createJwt (projectId, privateKeyFile, algorithm) {
// Create a JWT to authenticate this device. The device will be disconnected
// after the token expires, and will have to reconnect with a new token. The
// audience field should always be set to the GCP project ID.
const token = {
'iat': parseInt(Date.now() / 1000),
'exp': parseInt(Date.now() / 1000) + 20 * 60, // 20 minutes
'aud': projectId
};
const privateKey = fs.readFileSync(privateKeyFile);
return jwt.sign(token, privateKey, { algorithm: algorithm });
}
app.get('/', function(req, res){
let authToken = createJwt('test-project', './keys/device-config.pem', 'RS256');
const options = {
url: 'https://cloudiot.googleapis.com/v1/projects/test-project/locations/us-central1/registries/dev-registry/devices/test-device',
headers: {
'authorization': 'Bearer ' + authToken,
'content-type': 'application/json',
'cache-control': 'no-cache'
},
json: true
}
request.get(options, function(error, response){
if(error) res.json(error);
else res.json(response);
})
});
For backend servers to interact with IoT-Core, the authentication method is not the same as for device MQTT or HTTP connections. Reference: https://cloud.google.com/iot/docs/samples/device-manager-samples#get_a_device
I was able to retrieve and update device configurations using the code below
function getClient (serviceAccountJson, cb) {
const serviceAccount = JSON.parse(fs.readFileSync(serviceAccountJson));
const jwtAccess = new google.auth.JWT();
jwtAccess.fromJSON(serviceAccount);
// Note that if you require additional scopes, they should be specified as a
// string, separated by spaces.
jwtAccess.scopes = 'https://www.googleapis.com/auth/cloud-platform';
// Set the default authentication to the above JWT access.
google.options({ auth: jwtAccess });
const DISCOVERY_API = 'https://cloudiot.googleapis.com/$discovery/rest';
const API_VERSION = 'v1';
const discoveryUrl = `${DISCOVERY_API}?version=${API_VERSION}`;
google.discoverAPI(discoveryUrl, {}, (err, client) => {
if (err) {
console.log('Error during API discovery', err);
return undefined;
}
cb(client);
});
}
function getDevice (client, deviceId, registryId, projectId, cloudRegion) {
const parentName = `projects/${process.env.GCP_PROJECT_ID}/locations/${cloudRegion}`;
const registryName = `${parentName}/registries/${registryId}`;
const request = {
name: `${registryName}/devices/${deviceId}`
};
const promise = new Promise(function(resolve, reject){
client.projects.locations.registries.devices.get(request, (err, data) => {
if (err) {
console.log('Could not find device:', deviceId);
console.log(err);
reject(err);
} else {
console.log(data.config.binaryData);
resolve(data);
}
});
});
return promise;
}
app.get('/', function(req, res){
const cb = function(client){
getDevice(client, 'test-device', 'dev-registry', process.env.GCP_PROJECT_ID, 'us-central1')
.then(function(response){
let decoded = new Buffer(response.config.binaryData, 'base64').toString();
res.json(decoded);
})
.catch(function(error){
res.json(error);
})
}
getClient(serviceAccountJson, cb);
});
I think what you're looking to do is best accomplished using the client library for NodeJS.
First, retrieve an API client object as done in the sample. This will take in the service account credentials you used and will authenticate against Google API Core servers.
At the point in the referenced code where cb(client); is invoked, you'll have your client object and are ready to update your device. Add the imports and API constants from the sample and replace the code where you have a client object with the following code and you should be set.
Use some strings for your device identifiers:
const projectId = 'my-project';
const cloudRegion = 'us-central1';
const registryId = 'my-registry';
const deviceId = 'my-device;
const config = '{fan: 800}';
Next, form your device String:
const deviceId = `projects/${projectId}/locations/${cloudRegion}/registries/${registryId}/devices/${deviceId}`;
const binaryData = Buffer.from(config).toString('base64');
Now you form your request object and execute:
const request = {
name: `${registryName}`,
versionToUpdate: 0,
binaryData: binaryData
};
console.log(request);
client.projects.locations.registries.devices
.modifyCloudToDeviceConfig(
request,
(err, data) => {
if (err) {
console.log('Could not update config:', deviceId);
console.log('Message: ', err);
} else {
console.log('Success :', data);
}
});
Your configuration is updated. If your device is subscribed to the config topic on MQTT it will receive the latest configuration, otherwise, you can poll for the configuration with HTTP from your device.
Just to confirm, when you created the SSL key pair, and when you registered the device with the Cloud IoT Core registry, did you match the type of key created with the radio button you registered it with?
Also to confirm, you put the Google root certificate on the device in the same directory as the private key: ./keys/device-config.pem ? If not you can fetch it with: wget https://pki.google.com/roots.pem.

socket authentication in mobile app

I'm wondering how to properly authenticate a user using sockets and cordova.
Currently, the following happens:
User registers or logs in using an normal ajax command, sending email and password
I send back an access token and their email, which is stored in localstorage on the phone and as email => accesstoken in redis
The user then sends a connection request to the socket.io server, something like (client side):
var socket = io('http://192.168.50.3:8080/');
socket.on('connect', function(){
socket.emit('init', { email: 'email#email.com', token: '12ueno1' });
});
I then check on the backend if that access token and email are in my redis server, if they are, I start listening for any commands sent by the client like so (server side):
var io = require('socket.io').listen(8080),
mongoose = require('mongoose'),
schema = mongoose.Schema,
userModel = require('./models/user'),
lobbyModel = require('./models/lobby'),
redis = require('redis').createClient();
io.sockets.on('connection', function(socket){
socket.on('init', function(data){
// here, we check for an init command which will have an email and access token
// if it's there, we put the user into a room and let them wait for data
redis.get(data.email, function(err, reply){
if(reply === data.token){
// get into room
var roomKey = data.email;
socket.join(roomKey);
// get user
var user;
userModel.findOne({ email: data.email }, function(err, doc){
user = doc;
});
socket.in(roomKey).on('create_lobby', function(data){
// do stuff for creating a lobby and then send back the data
io.sockets.in(roomKey).emit('created_lobby', {
email: user.email
});
});
}
});
});
});
Now authenticated, the user can send commands like (client side):
socket.on('create_lobby', { name: 'test' });
Now, this works fine, I'm just wondering if I'm going at it the right way, or if I'm creating an insecure system.
Rather late but I've only just come across this question.
The answer is that the approach is insecure.
Firstly, the token isn't being validated.
Secondly, there is no timeout check.

Resources