Networked Media Week 9

User Logins

Now that we have tackled cookies and user sessions, let's turn our attention to developing a user registration and login system.

The general idea is that we allow users to register, keep a table or collection of them in a database, and when they log in we create a session for them so that they stay logged in as they browse the site.

A basic registration screen would be a form asking for the details we need for each user (registration.ejs):

<html>
  <body>
	<form action="/register" method="POST">
		Choose Username: <input type="text" name="username"><br />
		Choose Password: <input type="password" name="password"><br />
		<input type="submit" name="submit" value="Submit"><br />
	</form>		
  </body>
</html>
		
this would post to a route on our server to save the user's information:
app.post('/register', function(req, res) {
	// We want to "hash" the password so that it isn't stored in clear text in the database
	var passwordHash = generateHash(req.body.password);

	// The information we want to store
	var registration = {
		"username": req.body.username,
		"password": passwordHash
	};

	// Insert into the database
	db.insert(registration);
	console.log("inserted " + registration);
	
	// Give the user an option of what to do next
	res.send("Registered Sign In" );
	
});		
		
In the above route, we are using a password hashing function that uses bcrypt. In particular it is using this node module: bcrypt-nodejs. Here is the hash and the comparison function that we'll need:
function generateHash(password) {
	return bcrypt.hashSync(password);
}

function compareHash(password, hash) {
    return bcrypt.compareSync(password, hash);
}		
		
After the user registration information is saved in the database we can offer a login form (login.ejs) and a corresponding route to check that their information is correct:
<html>
  <body>
	<form action="/login" method="POST">
		Username: <input type="text" name="username"><br />
		Password: <input type="password" name="password"><br />
		<input type="submit" name="submit" value="Submit"><br />
		<a href="/registration">Not registered?</a>			
	</form>		
  </body>
</html>		
		
The route:
// Post from login page
app.post('/login', function(req, res) {

	// Check username and password in database
	db.findOne({"username": req.body.username},
		function(err, doc) {
			if (doc != null) {
				
				// Found user, check password				
				if (compareHash(req.body.password, doc.password)) {				
					// Set the session variable
					req.session.username = doc.username;

					// Put some other data in there
					req.session.lastlogin = Date.now();

					res.redirect('/');
					
				} else {

					res.send("Invalid Try again");

				}
			} 
		}
	);
	

});		
		
The key to logging a user in is to create the session variable, in this case their username. This session variable can then be checked on any page they visit to make sure they are logged in and if they aren't send them to the login page:
// Main page
app.get('/', function(req, res) {
	console.log(req.session.username);

	if (!req.session.username) {
		res.render('login.ejs', {}); 
	} else {
		// Give them the main page
  		//res.send('session user-id: ' + req.session.userid + '. ');
		res.render('main.ejs', req.session);
	}
});
		
To log a user out, simply delete their session variable:
app.get('/logout', function(req, res) {
	delete req.session.username;
	res.redirect('/');
});		
		

Here is a full example: express_login.zip

Other Login Systems

Doing a full login system can be quite a bit of work. Fortunately, there are solutions that exist which make it easier.

On that is pretty popular is called Passport. Passport offers a "local" login system (called a "strategy") with similar functionality to what we have created above. It also offers a variety of other "strategies" for using the login functionality of single sign on services such as Twitter or Facebook.

File Uploads

Allowing users to upload files with Express is fairly straight forward using the multer node module:

npm install multer
		
In your server, you'll need to require multer and then create an instance of it specifying any options such as where to store the files (in the code below, called "upload"):
var multer  = require('multer')
var upload = multer({ dest: 'uploads/' })
		
Then in any of your routes that you expect a file to be uploaded, you pass in "upload" and tell it what to expect. In this case, a single file that is named "photo" on the form:
app.post('/upload', upload.single('photo'), function (req, res) {
	console.log(req.file);
	res.send("uploaded: " + req.file);
	// req.file is the uploaded file information
  	// req.body will hold the other text fields
});
		
Doing file uploads in HTML requires using a bit of a different "form" tag, in particular a tag that specifies an "enctype" or encoding type of "multipart/form-data". Here is an example:
	
<html>
  <body>
    <form method="POST" action="/upload" enctype="multipart/form-data">
    	<input type="file" name="photo">
      <input type="submit" name="submitbutton" value="Submit" />
    </form>
  </body>
</head>