I am trying to handle two cases. When the connection is successful and not. I created a promise that should return if the connection is successful. But it does not handle the error (the connection does not work at all (I think the problem is authorization)).
If I enter the wrong username or password, then the connection does not work at all.
I need to handle the error, any ideas?
UPD. Body server.on is not called if i using wrong password or username. I think its feature mqtt connect or something.
return new Promise((resolve) => {
server = mqtt.connect('url', {
username: 'username',
password: 'pass'
});
server.on('connect', (res) => {
resolve(true);
server.end();
});
});
Related
I'm trying to set up an email verification flow in my project, but I can't seem to get it right.
How my flow works now is the user enters their credentials (email and password), which are used to create a new firebase user. Then, once that promise is resolved, it sends an email verification link to the new user that was created. The code looks like this:
async createUser(email: string, password: string) {
try {
console.log("Creating user...");
const userCredentials = await createUserWithEmailAndPassword(
auth,
email,
password
);
console.log("Successfully created user");
const { user } = userCredentials;
console.log("Sending email verification link...");
await this.verifyEmail(user);
console.log("EMAIL VERIFICATION LINK SUCCESSFULLY SENT");
return user;
} catch (err) {
throw err;
}
}
async verifyEmail(user: User) {
try {
sendEmailVerification(user);
} catch (err) {
throw err;
}
}
The link is sent through fine, but once I press on it, I'm redirected to a page that says this:
Strangely, the user's email is verified after this, in spite of the error message displayed. Any idea why this is happening?
Update:
I managed to figure it out. The email provider I'm using is my university's, and it seems to be preventing the verification link from working properly. I did try with my personal email to see if that was the case, but I wasn't seeing the verification link appearing there. I eventually realized that it was because it was being stored in the spam folder. It's working on other email providers, though, ideally, I'd want it to work on my university's email provider (the emails that users sign up with are supposed to be exclusively student emails). Any ideas how I could resolve this?
I eventually figured out that the issue was with my email provider. I was using my student email, which the university provides, and I imagine they've placed rigorous measures in place to secure them as much as possible. I have no idea what was preventing it from working, but I managed to figure out a workaround.
In brief, I changed the action URL in the template (which can be found in the console for your Firebase project in the Authentication section, under the Templates tab) to a route on my website titled /authenticate. I created a module to handle email verification. Included in it is a function that parses the URL, extracting the mode (email verification, password reset, etc.), actionCode (this is the important one. It stores the id that Firebase decodes to determine if it's valid), continueURL (optional), and lang (optional).
export const parseUrl = (queryString: string) => {
const urlParams = new URLSearchParams(window.location.search);
const mode = urlParams.get("mode");
const actionCode = urlParams.get("oobCode");
const continueUrl = urlParams.get("continueUrl");
const lang = urlParams.get("lang") ?? "en";
return { mode, actionCode, continueUrl, lang };
};
I created another method that handles the email verification by applying the actionCode from the URL using Firebase's applyActionCode.
export const handleVerifyEmail = async (
actionCode: string,
continueUrl?: string,
lang?: string
) => {
try {
await applyActionCode(auth, actionCode);
return { alreadyVerified: false };
} catch (err) {
if (err instanceof FirebaseError) {
switch (err.code) {
case "auth/invalid-action-code": {
return { alreadyVerified: true };
}
}
}
throw err;
}
};
The auth/invalid-action-code error seems to be thrown when the user is already verified. I don't throw an error for it, because I handle this differently to other errors.
Once the user presses the verification link, they're redirected to the /authenticate page on my website. This page then handles the email verification by parsing the query appended to the route. The URL looks something like this http://localhost:3000/authenticate?mode=verifyEmail&oobCode=FLVl85S-ZI13_am0uwWeb4Jy8DUWC3E6kIiwN2LLFpUAAAGDUJHSwA&apiKey=AIzaSyA_V9nKEZeoTOECWaD7UXuzqCzcptmmHQI&lang=en
Of course, in production, the root path would be the name of the website instead of localhost. I have my development environment running on port 3000.
Once the user lands on the authentication page, I handle the email verification in a useEffect() hook (Note: I'm using Next.js, so if you're using a different framework you might have to handle changing the URL differently):
useEffect(() => {
verifyEmail();
async function verifyEmail() {
const { actionCode } = parseUrl(window.location.search);
if (!actionCode) return;
router.replace("/authenticate", undefined, { shallow: true });
setLoadingState(LoadingState.LOADING);
try {
const response = await handleVerifyEmail(actionCode!);
if (response.alreadyVerified) {
setEmailAlreadyVerified(true);
onEmailAlreadyVerified();
return;
}
setLoadingState(LoadingState.SUCCESS);
onSuccess();
} catch (err) {
console.error(err);
onFailure();
setLoadingState(LoadingState.ERROR);
}
}
}, []);
It first checks if there is an action code in the URL, in case a user tries to access the page manually.
The onSuccess, onFailure, and onEmailAlreadyVerified callbacks just display toasts. loadingState and emailAlreadyVerified are used to conditionally render different responses to the user.
I am trying to create a project which allows the user to enter "email", "username" and "Password" to register to the site,
When I try to enter a user using the "username", email" and "password" to enter this site, I get the following error:
Backend server is running
not connected
C:\Users\odewo\chat-app\NODE-REST-API\node_modules\mongoose\lib\drivers\node-mongodb-native\collection.js:149
const err = new MongooseError(message);
^
MongooseError: Operation `users.insertOne()` buffering timed out after 10000ms
at Timeout.<anonymous> (C:\Users\odewo\chat-app\NODE-REST-API\node_modules\mongoose\lib\drivers\node-mongodb-native\collection.js:149:23)
at listOnTimeout (node:internal/timers:557:17)
at processTimers (node:internal/timers:500:7)
Below is my mongoose code:
mongoose.connect(process.env.MONGO_URL, {
userNewUrlPaser: true,
useUnifiedTopology: true,
useCreateIndex: true,
})
.then(() => {
console.log('Connected to MongoDB');
})
.catch((e) => {
console.log('not connected');
});
This is auth.js code for routes:
const router = require('express').Router();
const User = require('../models/User');
// REGISTER
router.get('/register', async (req, res) => {
const user = await new User({
username: 'samson',
email: 'samson#gmail.com',
password: '123456',
});
await user.save();
res.send('ok');
});
module.exports = router;
I will really appreciate your help
Monogo Db Atlas -> Network Access -> IP Access List -> IP address updated to access from any where works for me.
Reason : My Internet provider allots dynamic IP and every time i login the IP Address changes and this throws error.
Check your MONGO_URL and make sure your password or username is not wrapped in < > tags. I think your code is fine but its obvious that there are some problems. I would be checking the .env file to make sure.
The problem is not solved by removing the await. It is better to implement this function asynchronously. You must log in to your mongo account and add your IP address from the Network Access part.
you should remove the await before the new User declaration , use await just when you are waiting for the result of a promise like in await user.save()
You can solve this problem by using a simple password for your atlasDatabase meaning avoid using any special characters in your password like "#" or "$" and if you use then they must be url encoded that's why it is better if you avoid special characters and it worked for me and I hope it will work for you too.enter image description here
I am currently trying to test my node api w/ mocha chai. I am running into a scenario where a test should actually fail but is passing. I have a repo up of the current API that I am building here if you want to play around with it: enter link description here. However, I am still going to walk through the code in this question.
I'm trying to test the controller with the following code:
import chai, { expect } from 'chai';
import chaiHttp from 'chai-http';
import server from '../../src/app';
chai.use(chaiHttp);
describe('Authentication Controller', () => {
const testUser = {
email_address: 'test#test.com',
password: 'test'
};
describe('login success', () => {
it('responds with status 200', done => {
chai.request(server)
.post('/api/auth/login')
.send(testUser)
.end((err, res) => {
expect(res).to.have.status(200);
done();
});
});
});
describe('login failure', () => {
it('responds with status 401', done => {
chai.request(server)
.post('/api/auth/login')
.send(testUser.email_address = 'fake#news.com')
.end((err, res) => {
expect(res).to.have.status(200);
done();
});
});
});
});
Obviously I want to test a successful login and a failed login attempt. However, both the response statuses from the server are 200 and this should not be the case. When testing in Postman the response status when an individual tries to login with an email address that doesn't exist or a password that doesn't match, it returns a status of 401. If I write a test
expect(1).to.equal(1) => test passes.
expect(1).to.equal(2) => test fails.
Here is the controller function that handles the request for logging in:
export function login(req, res) {
User.findOne({email: req.body.email})
.then(user => {
if (user && bcrypt.compareSync(req.body.password, user.password)) {
generateToken(res, user);
} else {
res.status(401).json({
success: false,
message: 'Incorrect username or password.'
});
}
})
.catch(err => {
res.json(err);
});
}
The model that handles the request:
export function createUser(req) {
return db('users').insert(Object.assign(req.body,{password: hashPassword(req.body.password)}))
.then((id) => db('users').select().where('id', id).first());
}
As you can see I am using Knex.js. I have setup a test database and everything is connected appropriately, so I'm confused as to why my server is responding w/ a 200 response status when testing?
I just want to say thanks to anyone who takes the time to help me understand how mocha chai is working. I have very LITTLE experience with testing applications, but I want to start familiarizing myself w/ doing so because I believe it to be good practice.
I actually cloned your Github repo and tried running the test. From what I have seen, there are a couple of different issues in your code, as followed:
1. from the controller function you posted in the question:
```
export function login(req, res) {
User.findOne({email: req.body.email})
.then(user => {
// [removed because of little relevancy]
})
.catch(err => {
res.json(err);
});
}
```
The issue is the line res.json(err) which actually responded with a 200 status code (even though it was an error in this case). This is because res.json does not automatically set the HTTP response status code for you when you "send an error object". This fooled the client (in this case, chai-http) into thinking it was a successful request. To properly respond with an error status code, you may use this instead: res.status(500).json(err)
It's also worth noticing that some of your other controller functions got into this issue too.
2. from your userModels.js file, line 10, which is:
```
return db('users').select().where(req).first();
```
You are using Knex API in an incorrect way. It should be ...where('email', req.email)... This was the initial reason why your requests failed.
3. you set up your unit tests in different manners:
Test no. 1 (login success):
```
chai.request(server)
.post('/api/auth/login')
.send(testUser)
```
Test no. 2 (login failure):
```
chai.request(server)
.post('/api/auth/login')
.send(testUser.email_address = 'fake#news.com')
```
So, what happened?
In the first test, you passed an object into .send(), whereas in the second test, you simply passed an expression. When done this way, the model handler, userModels.findOne(), received an object with keys email_address and password for the first test, but for the second test, it did not.
Also, in your 1st test case, you sent testUser.email_address, but in your controller function, you referenced req.body.email.
All these, in addition to the issue no. 1 as I mentioned earlier, further complicated your test suite, leading to your misunderstanding in the end.
Disclaimer:
All what I wrote above was based on the source code from your Github repo, so if you have fixed some issues since you pushed your code, and some (or all) of my points above are no longer valid, please disregard. Nevertheless, I wish you have found, or will soon find out why your code didn't behave as you expected!
Cheers,
I just wanted to post an answer here that is specific to my experience and what helped me get all of this setup. The only thing that I really needed to change on the Repo Proj was the property email on the user object I was passing. I had email_address and was thus searching for that column in the database whilst it did not exist! So once I changed that I started down the right path.
I was then able to get my failed login test to pass. However, my successful login didn't pass. The reason was because I was seeding my database with a plain string password. Thus, when I performed the conditional statement of:
if (user && bcrypt.compareSync(req.body.password, user.password))
It wasn't passing because the bcrypt.comparSync was looking for a password that was hashed. In order to get this to work I needed to require babel-register in my knex file. This then allowed me to use es6 and perform my hashPassword function:
test/userSeed.js
import hashPassword from '../../src/helpers/hashPassword';
exports.seed = function(knex, Promise) {
return knex('users').truncate()
.then(() => {
return knex('users')
.then(() => {
return Promise.all([
// Insert seed entries
knex('users').insert([
{
first_name: 'admin',
last_name: 'admin',
email: 'admin#admin.com',
password: hashPassword('test')
},
{
first_name: 'test',
last_name: 'test',
email: 'test#test.com',
password: hashPassword('test')
}
]),
]);
});
})
};
hashPassword.js
import bcrypt from 'bcrypt';
export default function(password) {
const saltRounds = 10;
let salt = bcrypt.genSaltSync(saltRounds);
return bcrypt.hashSync(password, salt);
}
This resulted in the hashing of my users password when I seeded the DB. Tests all pass as they should and api works as intended using Postman.
This code isn't working, it never logs Ready! to the console. It's also not logging out any errors, so I believe it may have logged in correctly but just isn't working. Does anyone have any pointers?
var Discord = require("discord.js");
var client = new Discord.Client();
client.login('myEmail', 'myPassword', output);
client.on('ready', () => {
console.log("Ready!");
});
function output(error, token) {
console.log("errorz!");
if (error) {
console.log("There was an error logging in: ${error}");
return;
} else {
console.log("`Logged in. Token: ${token}`");
}
}
Just to make sure I am doing this right, I have put this in a file called discord-fisher.js and I am running it using node discord-fisher.js from the terminal.
Per the documentation for Discord.js for Client#login:
.login( token )
Logs the client in, establishing a websocket connection to Discord.
So when you use login, you don't provide your email or password details, you provide your API token associated with your account.
I'm fairly new to Meteor and trying to grasp its concepts. I have a client code below that triggers Meteor method to create new user:
Template["signup-team"].onRendered(function(){
var validator = $('.signup-team-form').validate({
submitHandler: function(event){
var email = $('[name=email]').val();
var password = $('[name=password]').val();
Meteor.call('addNewUser', email, password, "team-captain", function(error, result) {
if (error){
return alert(error.reason);
}
Router.go("complete-signup");
});
}
});
});
The method is defined to run on both client and server. When run on the server I want it to create user and add role to account. On the client side I want to sign user in.
Meteor.methods({
addNewUser: function(email, password, role) {
check(email, String);
check(password, String);
if(Meteor.isClient){
Accounts.createUser({
email: email,
password: password,
profile: {
completed: false
}
}, function(error){
if(error){
console.log(error); // Output error if registration fails
} else {
console.log(Meteor.userId());
}
});
} else {
var id = Accounts.createUser({
email: email,
password: password,
profile: {
completed: false
}
});
console.log(id);
Roles.addUsersToRoles(id, role);
}
}
});
The server part runs fine and new user is created but on client side I get error Error: No result from call to createUser and user isn't signed in automatically.
I assume the problem is I dont need to run createUser on the client and use Meteor.loginWithPassword instead but I would really like to know the theory behind this. Thanks
Don't do this. You are rewriting core code and creating security issues needlessly.
Instead of using your addNewUser method, just call Accounts.createUser on the client. Have a onCreateUser callback handle adding the role.
In your code, you are sending the users password to the server in plaintext. When you call Accounts.createUser, the password is hashed before being sent to the server. It also takes care of logging in the new user for you.
One gotcha with adding the role though, you will not be able to use Roles.addUsersToRoles(id, role) in the onCreateUser callback, as the user object has not yet been added to the database, and does not have an _id. However you can directly add the role to the proposed user object like this:
Accounts.onCreateUser(function(options, user) {
user.roles = ['team-captain']
return user;
})
Then again, maybe you don't want all users to be team captains!