You are on page 1of 13

Building a Login System in Node.

js and MongoDB
June 4, 2012 Javascript | Node.js 62 Comments

SEARCH

This past week I finally got around to playing with Node.js and am really impressed with how simple it was to get a server and database up and running in literally minutes. Once there, I thought a good first project to explore the platform would be to try building a simple login system analogous to what I feel like Ive built a million times in mysql & php. So after thinking about it a bit, I knew these were the basic features I wanted to implement :

Categories
Actionscript / Flash General News Node.js Processing Arduino Javascript OpenFrameworks Python Travel

Login Panel
Just a simple login panel with the ability to reset a lost password and of course the ability to create an account for first time users. An option to remember me that saves the users data in a local cookie allowing them to bypass the login screen whenever they revisit the site.

Raspberry PI Unix

Recent Twitter
31 March 2013 at 10:45pm

Follow @braitsch

373 followers

@sansumbrella will do, cheers mate!

@sansumbrella gotcha, thanks dude. really appreciate the nod. i was really scratching my head over this.
31 March 2013 at 10:43pm

@sansumbrella ah thanks! question tho would that require every triangle to have unique vertices? even if they're in the same position?
31 March 2013 at 10:40pm

Recent Posts
Controlling 24 LEDs with Node.js & a Raspberry PI How to Setup Node.js on a Raspberry PI Why Isnt Stylus Compiling my CSS? Internet Service Provider Health Monitor Building a Node.js Chat Application and Sharing Socket.IO Across Multiple Subdomains

New Account Screen


Asimple form that allows a new user to set their username & password, as well as some personal data such as their real name, email and location.

Popular Posts

converted by Web2PDFConvert.com

Transform Tool - Drag, Scale and Rotate in Flash at Runtime 66 comments Building a Login System in Node.js and MongoDB 62 comments Dynamically Create an Image in Flash and Save it to the Desktop or Server 56 comments

And Some Recent Snaps on Flickr...

Account Update Screen


Almost identical to the account creation screen, with the slight modification that the username field is deactivated so it cannot be changed allowing us to preserve a unique key to identify each account. Here well also add a button that will allow users to delete their account.

Password Retrieval
Asimple modal window where users can request an email to reset their password.

converted by Web2PDFConvert.com

If youd like to jump ahead : check out the full working version of the app or download and install the source code here

Standing on the Shoulders of Giants


One of the most impressive highlights of the Node.js ecosystem in my opinion is the incredible myriad of libraries actively being produced to take much of the heavy lifting out of building your app. In our login system well leverage the following libraries to get us up and running : Express.js ANode.js framework with a ton of convenient features that make working in Node much faster MongoDb ANoSQL database well use to save our account data Jade Atemplating engine that allows us to write less verbose HTML Stylus ACSS-preprocessor with a zillion amazing features that greatly take the pain out of writing traditional CSS Email.js Middleware to easily dispatch emails from our Node.js server Moment.js Alightweight library for convenient date parsing & formatting And last but not least the inimitableTwitter Bootstrap UI library to layout our forms and pages with beauty and consistency across browsers.

Application Structure
Our login system will of course need to execute code in two environments, on the client machine and on the server. On the client side well need to display our HTML pages, handle user interactions, and validate the various forms our app uses before sending their data to the server. On the server side well layout our HTML pages using Jade templates and create a few custom modules to read and write to the database and dispatch emails for password retrieval. The general layout of these two environments is as follows : Server-Side Components : views jade templates that compile to HTML login.jade home.jade signup.jade modules helper classes that interact with the database and dispatch emails account-manager.js email-dispatcher.js Client-Side Components : views these setup our form controllers & modal windows login.js home.js signup.js controllers handle user interactions loginController.js homeController.js signupController.js

converted by Web2PDFConvert.com

form-validators validate forms and display errors loginValidator.js accountValidator.js resetValidator.js emailValidator.js Note : Because the new account and update account forms are so similar, Ive consolidated the code that validates them into one file called AccountValidator and then put any code that differs between them in their respective controllers SignupController & HomeController.

So How Does All This Actually Work?


The basic page flow can be generalized into two parts: Part 1 : Getting the Page 1. Auser arrives at http://node-login.braitsch.io/ and requests the root page or / 2. Router.js on our server sees this GET request and returns login.jade, the view associated with http://node-login.braitsch.io/ However before it does this, it checks the GET request object for a username & password cookie and if they exist and validate, redirects the browser to http://node-login.braitsch.io/home

var AM = require('./modules/account-manager'); app.get('/', function(req, res){ // check if the user's credentials are saved in a cookie // if (req.cookies.user == undefined || req.cookies.pass == undefined){
res.render('login', { locals: { title: 'Hello - Please Login To Your Account' } } ); } else{

// attempt automatic login //


AM.autoLogin(req.cookies.user, req.cookies.pass, function(o){ if (o != null){ req.session.user = o; res.redirect('/home'); } else{ res.render('login', { locals: { title: 'Hello - Please Login To Your Account' } } ); } }); } }); 3. Otherwise, the server renders login.jade into the HTML login form and sends it to the browser. 4. Once the HTML is received by the client, the script tags in the page request the JavaScript files associated with the login page, namely : /js/views/login.js /js/controllers/loginController.js /js/form-validators/loginValidator.js /js/form-validators/emailValidator.js 5. These four component files setup the form and alert windows, listen for user interaction and validate the form before sending it back to the server. Part 2 : Posting the Page 1. Auser enters their username & password and hits submit 2. loginValidator.js validates the form and then allows login.js to send its contents to the server as a POST request. 3. Router.js on the server sees the incoming POST request and forwards the username & password to the AccountManager module which compares what the user entered to the values stored in the database. Once Router.js gets a response from the AccountManager it either sends a 200 (pass) or 400 (fail) status code back to the browser.

var AM = require('./modules/account-manager'); var EM = require('./modules/email-dispatcher'); app.post('/', function(req, res){ if (req.param('email') != null){ AM.getEmail(req.param('email'), function(o){ if (o){
res.send('ok', 200); EM.send(o, function(e, m){ console.log('error : '+e, 'msg : '+m)}); } else{

converted by Web2PDFConvert.com

res.send('email-not-found', 400); } }); } else{

// attempt manual login //


AM.manualLogin(req.param('user'), req.param('pass'), function(e, o){ if (!o){ res.send(e, 400); }else{ req.session.user = o; if (req.param('remember-me') == 'true'){ res.cookie('user', o.user, { maxAge: 900000 }); res.cookie('pass', o.pass, { maxAge: 900000 }); } res.send(o, 200); } }); } }); 4. login.js which owns the login form, hears the returned value and either redirects the user to the logged in page, or shows an alert window that displays a specific error message.

var lv = new LoginValidator(); var lc = new LoginController(); // main login form //


$('#login-form').ajaxForm({ beforeSubmit : function(formData, jqForm, options){ if (lv.validateForm() == false){ return false; } else{

// append 'remember-me' option to formData to write local cookie //


formData.push({name:'remember-me', value:$("input:checkbox:checked").length == 1}) return true; } }, success : function(responseText, status, xhr, $form){ if (status == 'success') window.location.href = '/home'; }, error : function(e){ lv.showLoginError('Login Failure', 'Please check your username and/or password'); } });

This communication sequence between the server and the client is essentially what is happening on each page of our app. In Summary 1. The user arrives at a page 2. Router.js returns the appropriate Jade template that renders the pages HTML and loads its JavaScript controllers 3. The user interacts with the page typically by filling out and submitting a form 4. Some JavaScript validates the form and sends its data back to the server 5. Router.js forwards the data to the AccountManager for comparison, entry, deletion, etc. in the database 6. The AccountManger returns a pass or fail response back to Router.js which then gets sent back to the client 7. JavaScript on the client handles the response by either redirecting the browser or showing a modal window with a detailed response. As you can see the communication between the client and server is not terribly complicated however it does beg the question as to how to best organize this communication into components and modules. So with that in mind, the source for this app is fully available on github with instructions on how to install and get this running on your local machine. As always feedback, questions and suggestions for improvement are most welcome.
ALSO ON THE CHOREOGRAPHY OF COLOR AND CODE AROUND THE WEB

How to Drain Liquid From Behind the Ear Drum What Food To Avoid If You Have Acid Reflux VIDEO: Path To The Pros 2013: Montee Ball Are You Forgetting
converted by Web2PDFConvert.com

Consumer Car Reviews

61 comments
Leave a message...
Best Community

Andrew Eremenko

2 months ago

Thank you for this great app. But I have some questions. Why did you store login and password in cookies? It's not secure, isn't it? How about idea to make auth token (or whatever) like at twitter or facebook?
18

Reply

Share

Donald Trap
5

5 months ago

figured it out. had to start mongodb before running node app


Reply

Share

bren101

3 months ago

Two small suggestions: 1. Upgrade to bcrypt for more robust salt & hash quality, and less lines of code as a bonus. 2. Write the time/date to mongodb as a native Date() object, so that it translates into a native mongodb ISODate structure.
2

Reply

Share

Declan E.

5 months ago

Hi, Sorry to post again but I have one more question. I see you have used Bootstrap, and for anyone new to using node/jade I'm finding it quite difficult to apply the logic and workings of your project to a simple login system I'm trying to make. My system is a simple html page, with CSS dictating where the login link is, and the jquery/javascript file is a simple lightbox type idea. But I'm lost applying this to your project! How would I go about re-creating something similar without bootstrap, it's rather confusing for a newbie! Thanks again, Declan
3 1

Reply

Share

hugozap
1

3 months ago

Why is app.use(exp.static(app.root + '/app/server')); required? Doesn't it expose the server folder contents?
Reply

Share

braitsch Mod

hugozap 3 months ago

You're right that does and was a security vulnerability. It was there to allow access to vendor libs that were in /server/vendor. Those files have since been moved to /public/vendor as of v1.2.1
0

Reply

Share

Mulyana

6 months ago

Stephen, do you have any hints to get these login systems to work under Express 3.x? Thanks for nice tutorial :)
1

Reply

Share

braitsch Mod
0

Mulyana 3 months ago

Updated to express.js v3.0.6 as of v1.3.0


Reply Share

Bryan Nichols

7 months ago

Stephen, Another question for you. Can you explain what you are doing with this call: require('./app/config')(app, exp)? It looks like you are passing app and exp to the require call but after looking at the node API I am at a loss.

converted by Web2PDFConvert.com

I just want to say that not only is this an awesome project, I am really floored by the exceptional organization of it all. I am trying to build my first Node app and this gives me something to strive for. I wanted to better understand how you segregated everything because it makes the project so much more organized. Do you have any recommendations for learning Node or JS in general? I've got a few books on it, but i am finding that even the ones published last month have out of date source code due to the momentum of Node. Thanks so much!
1

Reply

Share

Stephen Braitsch

9 months ago

Hi Tara, thanks for asking. Feel free to use the code in any way you like. If you wind up using it in a public facing project, it'd be great if you posted a link here so we can check it out. Otherwise I've imposed no restrictions on how you can use this code. Enjoy.
1

Reply

Share

John F Dutcher

a month ago

It's such a nice sample. But oddly..if I install mongodb with npm...a mongodb folder is created in node_modules that has no bin folder and no mongodb.exe or mongo.exe with which to start the server. If I replace the mongodb folder in node_modules with an empty one and unzip the windows download from mongodb web site into it...all needed exe's are there and I can start the server db and the shell. the 'sever' folder of the app fail when trying to 'require' mongodb ( require ('mongodb').db
0

Reply

Share

Alex

2 months ago

Thanks for this practical example. I was testing the app out on localhost and tried a password rest. I could not reset my password successfully with the message "I'm sorry something went wrong, please try again." Assuming i have configured my smtp server setting correctly, what could be wrong here? On the node app.js screen: TypeError: Cannot read property 'email' of undefined ..blah blah I noticed your website did not have the smtp notification setup too. Many thanks in advance. :)
0

Reply

Share

Etna Mianuloe

2 months ago

Thanks for tutorial!! Trying to figure it out.. Hard as I am a beginner. In Part 2 point 2 loginValidator.js validates the form and then allows login.js to send its contents to the server as a POST request. What's the code for it in login.js?
0

Reply

Share

Mark Barton

2 months ago

Hi, Great example - thanks. One thing - on the updatePassword method in the account-manager.js file, I couldnt get it to return a HTTP 200 unless I amended the callback which was within the account.save call i.e. Originally it was this: accounts.save(o, {safe: false}, callback); I added the account object so something was returned back to the callng method. accounts.save(o, {safe: false}, callback(o)); I am new to node so I wasnt sure if this was correct - else I would have done the update via GIT. I am going to have a go at using mongoose as a learning exercise - do you foresee any issues? Thanks Mark
0

Reply

Share

Eric Fleischmann
0

2 months ago

This is righteous. Thanks for sharing. Will try to get it to work with mongoose and bcrypt for my application.
Reply

Share

tylerh
0

3 months ago

This is sweet thanks for posting this!

Reply

Share

converted by Web2PDFConvert.com

bren101
0

3 months ago

For security, shouldn't the validation code really be running on the server, not the client?

Reply

Share

braitsch Mod
0

bren101 3 months ago

Authentication is happening on the server, what are you talking about?


Reply Share

bren101

braitsch 3 months ago

I'm talking about all the code in form-validators. Apologies if I'm reading it wrong, but if a malicious user bypasses the client-side validation, doesn't it mean the server might do things such as write invalid email addresses into its database?
0

Reply

Share

braitsch Mod

bren101 3 months ago

Ah yes I see what you're talking about. Sure the client side validation just attempts to protect against common user errors such as an invalid email or empty form field. If you wanted to protect against something say like a SQL injection you'd want to add that sanitization in the AccountManager before allowing a write to the DB.
0

Reply

Share

flo
0

3 months ago

Tank's for sharing such great dev. Can somebody tell ma what print.jade and account.jade use for ?

Reply

Share

Jube

3 months ago

Hello Braitsch, Great tutorial. As a beginner I am not sure how did you host it in http://node-login.braitsch.io/ --Jube
0

Reply

Share

WaltDjr

3 months ago

Hi Stephen, I'm fairly new to node and everything is working great except the "Forgot your Password." error : code 5 error : previous { [Error: getaddrinfo ENOENT] code: 'ENOTFOUND', errno: 'ENOTFOUND', syscall: 'getaddrinfo' } error : smtp undefined I have changed the email-settings.js to point to my smtp server with my credentials. I've searched on this error and I'm not really finding anything. Can you point me in the right direction on where to start troubleshooting this? Thanks in advance and thanks for this example. It will definitely help get my feet wet in Node and Mongo along with the other modules you have used. WaltDjr
0

Reply

Share

Tim Coombs
0

4 months ago

Great use of nodejs and jade. I hadn't heard of jade, but now after a couple days of developing i can't not use it!
Reply

Share

Mark Coles

4 months ago

Hi Stephen I like the example, very clear and easy to follow, I am in the process of creating a new site with a user account aspect but will be using you are not using it, how easy would it be to update/ edit it to have it work with socket.io? Thank you for you advice.
0

Reply

Share

Stephen Braitsch

Mark Coles 4 months ago

@Mark Coles Socket.io is a very easy to work with. Here's another project I wrote that shows how to setup a simple chat application using it.
0

Reply

Share

JOhn terry

5 months ago

converted by Web2PDFConvert.com

Never mind, that was it!


0

Reply

Share

JOhn terry
0

5 months ago

Hey Donald, how did u start the monogodb? Did it not install as a dependency? I tried to start it with mongod
Reply

Share

Declan E.

5 months ago

Stephen, Great program. I have the program running on localhost:8080 to test on my home computer, I have registered an account an then tested it by logging in with it. Where do I now go to actually see the databases created by the mongodb connection? So that I can get a clearer idea of what your program is storing and where. Another question I have, when hosting this on the internet as you have done with your example for us to see, how do you go about this, for example on my home computer, I load cmd prompt...run mongo...then go to the login app's folder and run this...and connect in my browser using localhost:8080...How would you apply this to a real website? I ask this because of course on my computer at home I have mongo installed, nodejs installed, and all the other features you need for other extensions such as npm, bcrypt etc. Thanks, Declan
0

Reply

Share

WeLeven

5 months ago

Hi, just want to know is there an easy way to prevent the same user from logging in from different machines (same IP but different ports, or different IP altogether)? I tried to add state to each account of the user to tracked if they are logged in or out but it will fail if they close the browser without logging out first.
0

Reply

Share

Donald Trap

5 months ago

Thanks, I finally got it installed on Windows XP. However, now i'm experiencing an error within a few seconds of starting node app. Error: failed to connect to [localhost:27017]
0

Reply

Share

Donald Trap

5 months ago

Hi I'm currently having issues with the install. I followed all the requirements/dependencies but still get errors when I run 'npm install -d' below is copied from my npm-debug 189 info postuninstall bcrypt@0.7.3 190 error bcrypt@0.7.3 install: `node-gyp rebuild` 190 error `cmd "/c" "node-gyp rebuild"` failed with 1 191 error Failed at the bcrypt@0.7.3 install script. 191 error This is most likely a problem with the bcrypt package, 191 error not with npm itself. 191 error Tell the author that this fails on your system: 191 error node-gyp rebuild 191 error You can get their info via: 191 error npm owner ls bcrypt 191 error There is likely additional logging output above. 192 error System Windows_NT 5.1.2600 193 error command "C:\\Program Files\\nodejs\\\\node.exe" "C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\npm-cli.js" "install" "-d" 194 error cwd C:\Documents and Settings\Administrator\My Documents\GitHub\node-login 195 error node -v v0.8.14 196 error npm -v 1.1.65 197 error code ELIFECYCLE 198 verbose exit [ 1, true ]
0

Reply

Share

Stephen Braitsch

Donald Trap 5 months ago

I had an error once attempting to download bcrypt due to one of the servers hosting it being offline. suggestions, or try installing it again in a few hours.
0

Reply

Share

converted by Web2PDFConvert.com

Donald
0

5 months ago

Would it be difficult to implement a captcha/recaptcha system to prevent spam?

Reply

Share

Andrei Soare
0

5 months ago

Thanks, Stephen! Great intro to node.js for us beginners ;)


Reply

Share

Ali Hassan

6 months ago

Also, once I have unchecked the "remember me" checkbox and then I logout and return back to the login screen here still the "remember me" checkbox appears checked?
0

Reply

Share

Ali Hassan

6 months ago

Is it should be the desired behavior? It should be in such way that when I have unchecked "remember me" checkbox and then I quit the browser, then It should ask be to login again as its session has expired? Please correct me.
0

Reply

Share

Ali Hassan

6 months ago

Hi STEPHEN, That is AWESOME!!!! I am making an website using the same technology that you have used, so It is really really helpful for me. BUT:( One thing in which I am stuck is why the "Remember me" check is not working as desired:( I unchecked the "Remember Me" check and then quit my Mozilla Firefox but when I reopen your home screen " redirected me to the home page whereas, I was expecting that It will redirect me to the login screen to login again but It did not:(
0

Reply

Share

Stephen Braitsch
0

Ali Hassan 6 months ago Share

You have to manually logout for the "remember me" option to stop taking effect.
Reply

Mihai

6 months ago

Could i just ask why i should use my own (copying yours :P) login model instead of using something like passport? just want to know what advantages/disadvantages that might mean since im just starting with node.js
0

Reply

Share

Stephen Braitsch

Mihai 6 months ago

@mihai Use whatever works for your project, although understanding how I approached this problem may help you learn something. @mulyana I unfortunately have not had the time to port this to Express 3. If you want to fork it and submit a pull request that would be fantastic.
0

Reply

Share

Florent Wozniak

Stephen Braitsch 3 months ago

Hi Stephen, I'm new to Node and I'd like to ask you what are the modifications that need to be done to port your code to Express 3 ? I struggle for the last two day with it wo success :-( I've made lots of search in google and also asked in Stakeoverflow forum but with no response. Express and firebug return to me no error. It is very frustrating because your code works nicely with Express 2. Thank you for your reply (even a clue would be great). Flo
0

Reply

Share

braitsch Mod

Florent Wozniak 3 months ago

It shouldn't require much to migrate the app to Express3, it's on my to do list but I haven't had the time. If you are able to successfully migrate the project, go ahead and issue a pull request.
0

Reply

Share

braitsch Mod

braitsch 3 months ago

converted by Web2PDFConvert.com

I just updated the app to Express.js v3.0.6 as of v1.3.0.


1

Reply

Share

Florent Wozniak

braitsch 3 months ago

Many thinks Braitsch i really appreciate. I guess you now use 'extends' instead of layout in .jade file (got it) :) did you make other changes ?
0

Reply

Share

daslicht

6 months ago

That you for this wonderful example. In you example you store the username and the password in the cookie. How about using a Session to remember a user ? e.g.: https://github.com/visionmedia... In that case only a session ID would be stored in the cookie. Woundnt this be more secure? Or is that session ID some easy to fake? generate ? What do you think ?
0

Reply

Share

Bryan Nichols

7 months ago

Stephen, I think I figured it out. require is returning a function and you are calling that function immediately and passing in app an exp. Very clever way to structure code. I am going to try and fork your repo to see if i can get it to work with mysql. If anything, it's awesome to look through your code base; I want to aspire to aspire to this level of coding. Thanks again for writing this tutorial, I really appreciate it.
0

Reply

Share

Stephen Braitsch
0

Bryan Nichols 7 months ago Share

Thanks Bryan for the feedback, glad to hear you found this useful. Cheers!
Reply

Jake

7 months ago

Stephen - Great tutorial and thanks for sharing. I'm just beginning to learn javascript and node and am a bit unclear on the use variables 'o','e',and 'd' inside the anonymous functions called from account-manager and router.js. Can you help me better understand where these variables are first instantiated, assigned, and how they function within the program? Appreciative of any additional information you can provide.
0

Reply

Share

Stephen Braitsch

Jake 7 months ago

Hi, those are just the incoming arguments of the function. They are mapped to whatever values you pass to the function when you call it. HTH
0

Reply

Share

Load more comments

Comment feed

Subscribe via email

converted by Web2PDFConvert.com

converted by Web2PDFConvert.com

converted by Web2PDFConvert.com

You might also like