What is the best approach to validate user creation on the client and server?
I've tried to validate user creation both server and client side. First I've used methods and Accounts.createUser function but it didn't work even that the documentation says it should.
I've tried different approach. I used Accounts.createUser to validate it on the client and Account.onCreateUser on the server. The problem is that I can't validate password because it's encrypted.
So what is the best way to do it?
For validation of new users, see here
Example from the docs:
// Validate username, sending a specific error message on failure.
Accounts.validateNewUser(function (user) {
if (user.username && user.username.length >= 3)
return true;
throw new Meteor.Error(403, "Username must have at least 3 characters");
});
// Validate username, without a specific error message.
Accounts.validateNewUser(function (user) {
return user.username !== "root";
});
Related
I'm a newbie at node.js, and trying to make a login page.
When I'm trying to login with the right credentials the page works as intended. Also worth noting is that when I try to login with no credentials the alert window pops up. But when I try to login with the wrong credentials, it just won't work. I've tracked the error back to "alert" function, but I cannot understand why in the world the alert function works when calling it to display the alert that no username nor password was inserted, but will crash when I'm trying to display that the username and password are wrong. I'm pretty certain that the error comes from the alert function.
The code is below.
Thanks alot!
app.post('/auth', function(request, response) {
var username = request.body.username;
var password = request.body.password;
if (username && password) {
connection.query('SELECT * FROM accounts WHERE username = ? AND password = ?', [username, password], function(error, results, fields) {
if (results.length > 0) {
request.session.loggedin = true;
request.session.username = username;
response.redirect('/home');
response.end();
} else {
alert('Incorrect Username and/or Password!');
}
});
} else {
alert('Please enter Username and Password!');
}
});
but I cannot understand why in the world the alert function works when calling it to display the alert that no username nor password was inserted
It shouldn't do.
alert isn't a function provided by Node.js. It belongs to browsers so it can show something in the browser UI.
To send something from a server built on Node.js back to the browser then:
The something needs to be data
You need to send it by calling a method (like render, json or send) on the response object.
My solution works, but I'm not sure this is safe and appropriate. On the front end I have a ReactJS app that send with axios a request with the login and password. On the back end I have NodeJS + ExpressJS handling the request as follows:
router.post('/', function(req, res, next) {
// get the records that match the login provided
const sql = "SELECT name, surname, login, password, blocked FROM users WHERE login=?";
query(sql, [req.body.login])
.then((result)=> {
// if there are 1 or more results, compare the passwords with bcrypt
if (result.length > 0) {
bcrypt.compare(req.body.password, result[0].password, function(err, success) {
if (success) {
// if the user is not blocked, send the status 200 with user's data
result[0].blocked ?
res.status(401).json({type: 'Warning', message: 'Your account has been blocked. Plase contact the admins.'})
:
res.status(200).json({name: result[0].name, surname: result[0].surname, email: result[0].email});
} else {
// send an error if the password is wrong
res.status(401).json({type: 'Error', message: 'Please check that your login and password are correct.'});
}
});
} else {
// send an error if the login was not found
res.status(401).json({type: 'Error', message: 'Please check that your login and password are correct.'});
}
});
});
Is it enough/safe to query the db for the provided login (it's unique) with if (result.length > 0)?
Is it ok to have the error message contained in the server response like this?
res.status(401).json({type: 'Warning', message: 'Your account has been blocked. Plase contact the admins.'})
I have the chance to let the user know if he typed the correct login but the wrong password; should I let him know that? I think it would give to malicious users the knowledge that the login actually exists, so for now I just send a generic login/pwd error. Is this ok?
Is ok to send the user's data from the server to the client if the login was successful?
Is it ok to have the error message contained in the server response like this?
I have the chance to let the user know if he typed the correct login but the wrong password; should I let him know that? I think it would give to malicious users the knowledge that the login actually exists, so for now I just send a generic login/pwd error. Is this ok?
Your implementation is good enough. It's also a good practice letting users know why they are unable to login without giving out too much information EVEN when it's a problem with their supplied credentials (something you are doing already).
Is it enough/safe to query the db for the provided login (it's unique) with if (result.length > 0)?
Yes, this is fine too. You may also want to add a LIMIT 1 to your query to give you a little performance boost since there is no point having your DB scan through all the records when you expect only one result.
It is also a good practice to only send the minimum amount of information and request for more on demand.
As a general observation of your code, you would benefit from the following:
Doing some error checking on your request object before querying the database at all (good practice too) as there is no guarantee that a valid or well formatted username/password would be sent with the request.
Moving the responses into another file to make your code cleaner and maintainable.
I am making an authentication form and I would like to verify silently an email address is active on the client side. That is if the email address exists, then return true, else return false. This does not involve sending an actualy email to the address. I can do this on the server side using email-verify package in node, ie:
server.post('/api/verify-valid-email-silently', (req, res) => {
if (req.body && req.body.email) {
const email = req.body.email
email_verifier.verify( email, (err : string, info : any) => {
// do something
})
}
}
But I would like to do this on the client side so that I don't have to ping the server and pay for cloud function invocation. Again I'm looking for a free service on the client side. This is important because if I use the current "ping the server" way, someone could conceivably repeatedly enter inactive but well-formed email address and drain my bank account dry completely.
It does require a validation email, but it can be done without maintaining server-side infrastructure. I actually built a platform to do exactly that at https://clicktoverify.net/.
Essentially you just need to add our (small) javascript library to your page. Then you'll be able to send a verification email via our service and execute a client-side callback once the client verifies by clicking the link in their email.
I've set-up a Firebase project which I am using for it's user authentication module. I am also using the firebaseui-web project from Github.
My redirect on sign-on is working fine per this code:
// FirebaseUI config.
var uiConfig = {
'signInSuccessUrl': 'MY_REDIRECT.html',
'signInOptions': [
firebase.auth.EmailAuthProvider.PROVIDER_ID
],
// Terms of service url.
'tosUrl': '<your-tos-url>',
};
When the page loads (i.e. MY_REDIRECT.html) I'm checking the status of the user to see if they have verified their e-mail, and if not then invoke the sendEmailVerification method:
checkLoggedInUser = function() {
auth.onAuthStateChanged(function(user) {
if (user) {
// is email verified
if(user.emailVerified) {
// show logged in user in UI
$('#loggedinUserLink').html('Logged in:' + user.email + '<span class="caret"></span>');
} else {
// user e-mail is not verified - send verification mail and redirect
alert('Please check your inbox for a verification e-mail and follow the instructions');
// handle firebase promise and don't redirect until complete i.e. .then
user.sendEmailVerification().then(function() {
window.location.replace('index.html');
});
}
} else {
// no user object - go back to index
window.location.replace("index.html");
}
}, function(error) {
console.log(error);
});
};
window.onload = function() {
checkLoggedInUser()
};
All good so far - Firebase is doing what I want! Thanks guys :)
However, in the Firebase Console UI there doesn't appear to be a way of seeing if a user actually went to their inbox and clicked on the link to perform the verification. The UI looks like this:
I've run basic tests and the User UID doesn't change before and after verification has taken place.
So, here's my question - did I go about the e-mail verification correctly? If so (and therefore the UI doesn't show me verified vs unverified) is there an accepted method of accessing these two sets of users in the Auth module? I can't see the way to access the underlying table of UIDs and their properties (including the emailVerified property). I don't mind having to write more code if the functionality isn't in the Console - just looking to get nudged in the correct direction for my next step.
There is currently no way in the Firebase Console to see whether a specific user's email address has been verified. There is an API to get a list of users, but you can't filter on whether they're verified or not.
You can check whether the currently authenticated user's email address is verified with:
firebase.auth().currentUser.emailVerified
You cannot prevent who signs up. But you can easily ensure that only users with a verified email address can access (certain) data. For example:
{
"rules": {
".read": "auth != null && auth.token.email_verified",
"gmailUsers": {
"$uid": {
".write": "auth.token.email_verified == true &&
auth.token.email.matches(/.*#gmail.com$/)"
}
}
}
}
The above rules ensure that only users with a verified email address can read any data and only users with a verified gmail address can write under gmailUsers.
I am creating a web app with Meteor and i am having some concerns about the security on account creation. I know i can fix this problem by using a Meteor method, but the advantage of doing this client-side is that you can log the user in instantly after the account is created.
I currently don't have anything validating my input to make it simple:
Template.register.events({
'submit form': function (e) {
e.preventDefault();
var userInfo = {
email: e.target.registerEmail.value,
password: e.target.registerPassword.value,
password_confirmation: e.target.registerPasswordConfirmation.value
};
check(userInfo, {
email: String,
password: String,
password_confirmation: String
});
//Do some checks on the passwords and email before trying to create account
Accounts.createUser({
email: userInfo.email,
password: userInfo.password,
profileComplete: false
});
}
});
This works great and it's the way that the accounts-ui package does it. The problem is that i can bypass any input validation (and i checked this on an app running just the accounts-ui and accounts-password package too) by simply writing a javascript command in the console like this:
Accounts.createUser({
email: "NotAnEmail",
password: "123"
});
And this will create a user with the email and password as shown and log them in. This seems like a huge flaw with the accounts-ui package, or mabey i am just missing something? Is there some way to still allow client-side account creation, while making sure you can't do any javascript injections from the console to bypass the input checks?
For anyone having the same concerns about the security i discovered that there is a Meteor hook which you can use to validate the input after the Accounts.createUser request has been sent. This is how my code looks, when i validate the email field only and also add the extra field profileComplete.
Accounts.onCreateUser(function(options, user) {
if (! validateEmail(options.email))
throw new Meteor.Error(400, 'There was an error processing your request');
user.profileComplete = options.profileComplete;
return user;
});
validateEmail is a custom function i defined and not a native Meteor function.