Meteor accounts-password client-side account creation security - javascript

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.

Related

AWS Cognito Migrate User Lambda Force Reset Password

I have an issue I've been unable to resolve. I'm using AWS Cognito User Pools with a user migration lambda function to import users on the fly. After successfully validating a user's password, I'd like to check if this password meets the requirements Cognito enforces. As per the docs, this should be done in code:
Amazon Cognito doesn't enforce the password strength policy that you configured for the user pool during migration using Lambda trigger. If the password doesn't meet the password policy that you configured, Amazon Cognito still accepts the password so that it can continue to migrate the user. To enforce password strength policy and reject passwords that don't meet the policy, validate the password strength in your code. Then, if the password doesn't meet the policy, set finalUserStatus to RESET_REQUIRED.
However, when I set finalUserStatus to RESET_REQUIRED, I see behavior I don't understand. When logging in for the first time, I see the following, which seems correct:
At this time, I also receive an email with Subject: "Your temporary password" and body: "Your username is ...#gmail.com and temporary password is l6*NWOEp.". I thought this was odd because I hadn't submitted the above form yet. When I enter my email in the above form, I receive:
I don't receive any additional emails. When I fill out this form, I receive the same error.
When I go back to the sign in page and use the temporary password I received in the email, I am forwarded to the first form above: the one with the "Password reset required for user due to security reasons".
Below is the code, I've removed unrelated config and error handling:
export const lambdaHandler = async (event: any, context: any): Promise<any> => {
if (event.triggerSource == "UserMigration_Authentication") {
const userName = event.userName;
const password = event.request.password;
const user = await authenticateUser(userName, password);
event.response.userAttributes = {
"email": user.email,
"username": user.email,
"email_verified": "true",
};
if (passwordIsWeak(password) { // always true for testing purposes
event.response.finalUserStatus = "RESET_REQUIRED";
}
context.succeed(event);
}
};
The user in my Cognito User Pool has a Confirmation Status of "Reset required".
I've tried many combinations of finalUserStatus (RESET_REQUIRED, CONFIRMED, FORCE_CHANGE_PASSWORD) and messageAction (SUPPRESS, RESEND) returned in event.response. Anyway, thanks for any help provided!

Ionic + Phonegap - add offline database with authorization

I've recently made simple app using ionic framework (also with phonegap android/ios). I needed to store some user data in database. I found out about firebase and successfully integrated it in my app. I've let user log in anonymously or by google. In my case each user should see only his content so I've added rules - and they work. However my app needs to run without internet connection on mobile platforms (android/ios) and firebase doesn't provide full offline storage (only temponary) in javascript (persistent storage is for now implemented in native only). So basically I hit the wall. I've been told about CouchDB/PouchDB and it indeed support offline storage and sync with remote server. I've created an account on cloudant, but then I remembered I need user authorization - as I said user needs to have only his own data. So let's say guest have only local database and user logged in with google have database also on remote server so he can switch device and work with the same (his own) data. But I can't find such tool in CouchDB (cloudant). Is there any other solution, which works with ionic smoothly and provides:
offline database,
authorization with google,
remote database with sync (with local db) after successful authorization,
remote database is hosted on some server (may be paid, but trial needed) - i don't want to worry about server - client only
?
TL;DR: I'm looking for a hosting service working on ionic/angularjs/cordova with remote database + local database and synchronisation between them.
What is wrong with using MongoDB it is by far the easiest to integrate with Express/Node.js using the npm mongoose. It's really popular and great way to store data and its similar to firebase.
For example:
var Express = require('express'),
app = Express.router();
app.route('/api/auth/signup').post(users.signup);
Schema Example:
var mongoose = require('mongoose'),
Schema = mongoose.Schema
var UserSchema = new Schema({
firstName: {
type: String,
trim: true,
default: '',
required: 'Please fill in your first name'//message given if you fail to provide the required field.
},
lastName: {
type: String,
trim: true,
default: '',
required: 'Please fill in your last name'
},
displayName: {
type: String,
trim: true
},
email: {
type: String,
unique: 'Email already exists. Sign In?',
lowercase: true,
trim: true,
default: '',
validate: [validateLocalStrategyEmail, 'Please fill a valid email address'],//create a custom Email Validation strategy
required: 'Please enter a valid business Email'
},
});
Sample Controller:
exports.signup = function (req, res) {
// For security measurement we remove the roles from the req.body object
delete req.body.roles;
// Init user and add missing fields
var user = new User(req.body);
user.provider = 'local';
user.displayName = user.firstName + ' ' + user.lastName;
// Then save the user
user.save(function (err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
// Remove sensitive data before login
user.password = undefined;
user.salt = undefined;
var jwtToken = authentication.signToken(user);
res.json({ user: user, token: jwtToken });
}
});
};
You can find some of this on MEAN.js Github.
But you must use LocalStorage. JSON web tokens for your ionic app if you want it to work on an iPad/iPhone.
The best bet is to use PouchDB which works with local storage (works on ionic/cordova) and will sync to a remote couchdb server with just a few lines of code:
var localDB = new PouchDB('mylocaldb');
var remoteDB = new PouchDB('http://localhost:5984/myremotedb');
localDB.sync(remoteDB);
The only tricky bit is the remote server with authentication. CouchDB can handle oauth out of the box, but on a per-database case, meaning you need to create a database per user, which is what many people do, but that's quite involved. Do a search for CouchDB on npmjs.com and see what comes up.
Alternatively, you can wrap your CouchDB behind a node server like Express, and handle auth in Express before passing the requests through.
It's not an easy task, and there's a reason Firebase charges a small fortune once you start racking up usage. If you're not big on server-side stuff, a simpler approach might be to use meteor or hoodie.

Meteor's createUser running on client and server

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!

How to validate user creation on the server in Meteor

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";
});

how to store signup data into redis with node.js

i want to store signup data it contains name and email and password.
i will store this data in mongodb like this
db.save({"name":"xxxx","email":"xxxxx","password":'xxxxxxxx'},function(err,result){});
when user login ,they surely give their email id or username with password so i will find this user exist in db or not by using like this
db.find({'email:'xxxxxxx','password':'xxxxxxxxx'},function(err,result){});
i have tried to do same in redis,by like this
db.hmset('key' name xxxxx email xxxx pass xxxxxx,function(){});
it is stored but how can i check email id usename already exist becz user will give email and password only.if i know key then only i can find that data.even if i know key i can get only data i could not be found data already exist ot not like mongodb
how can i solve this?
You could store your users both in a Set and a Hash for details.
You can then check in the Set if a user exists with: http://redis.io/commands/sismember
I think you should break things down into chunks instead of trying to do everything with one query. So, for example, to add a new user, first check if the user exists:
(My example assumes a Mongoose User Model has been defined)
User.findOne({$or : [{'email': req.body.email}, {'username': req.body.username}],
function(err, result) {
if (err) {next(err);}
if (result) {
// you found an existing user
res.send(309, {'message':'Error: User exists'});
} else {
// no user found
var user = new User({
'email': req.body.email,
'username': req.body.username,
'password': req.body.password,
'and-so-on': req.body.moredata
});
user.save(function(err){
if (err) {next(err);}
res.send(200, {'message':'User Registered Successfully'});
});
}
Honestly though, I wouldn't recommend writing a user authentication system from scratch, because it is pretty limiting in todays world of auth methods. I personally use Passport, because it gives you the ability to use multiple auth systems with your app, including Facebook, Twitter, and so on and so forth.

Categories

Resources