Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ## User Authentication
- ### Authentication with Passport
- #### Beginning a new project
- Create a new directory named userauth and cd into that directory
- ```bash
- mkdir userauth && cd userauth
- ```
- Create the express application
- ```bash
- express -e
- ```
- Install the dependencies
- ```bash
- npm install
- ```
- ***Note at this point in time it would be wise to set up version control with git and github if you haven't already***
- #### Setting up the database
- Navigate to mongolab and lets get our database setup.
- In mongo lab we need to:
- 1. Create a new database
- 2. Create the database user
- 3. Get our Standard URI connection string to add to our bash profile.
- ***Remember these are users that will have access to use the database, and you WILL be using these as your user name and password replacements in your connection strings***
- Now we need to add our connection string with the appropriate username and password to our bash profile:
- ```bash
- subl ~/.bash_profile
- ```
- Add the connection string to the appropriate place in your bash profile in the following format:
- export DB_CONN_USERAUTH="mongodb://matt:matt@ds055515.mongolab.com:55515/userauth"
- ***DON'T FORGET THIS STEP... SOURCE YOUR BASH PROFILE OR RESTART THE TERMINAL APPLICATION COMPLETELY***
- ```bash
- source ~/.bash_profile
- ```
- Now lets install the mongoose package:
- ```bash
- npm install mongoose --save
- ```
- Lets add our connection to our app.js file now that we have all of the pieces:
- ```
- // app.js
- // ...
- app.use(logger('dev'));
- app.use(bodyParser.json());
- app.use(bodyParser.urlencoded({ extended: false }));
- app.use(cookieParser());
- app.use(express.static(path.join(__dirname, 'public')));
- // Mongoose connection
- var mongoose = require('mongoose');
- mongoose.connect(process.env.DB_CONN_USERAUTH);
- app.use('/', routes);
- app.use('/users', users);
- // ...
- ```
- Now that we have our app set up for our database lets get down to authenticating our users:
- #### Installing all of our dependencies
- We are going to have a few dependencies we need to install so lets do that first.
- We need to add passport, passport-local, bcrypt, connect flash, and express session.
- ```bash
- npm install passport passport-local bcrypt-nodejs connect-flash express-session --save
- ```
- Make sure we add the appropriate modules to our app.js file as well. **Note we do not need the bcrypt module or the passport local module here**
- ```js
- var express = require('express');
- var path = require('path');
- var favicon = require('serve-favicon');
- var logger = require('morgan');
- var cookieParser = require('cookie-parser');
- var bodyParser = require('body-parser');
- // Add passport
- var passport = require('passport');
- // Add connect flash
- var flash = require('connect-flash');
- // Add express session
- var session = require('express-session');
- var routes = require('./routes/index');
- var users = require('./routes/users');
- var app = express();
- ```
- Further, for passport we need to also add these lines into app.js file.
- ```js
- app.use('/', routes);
- app.use('/users', users);
- // Required for passport
- app.use(session({ secret: 'b1t3myshlnym3t@L@5s' })); // session secret
- app.use(passport.initialize()); //initialize passport
- app.use(passport.session()); // persistent login sessions
- app.use(flash()); // use connect-flash for flash messages stored in session
- // routes
- require('./routes/passport.js')(app, passport); // load our routes and pass in our app
- // catch 404 and forward to error handler
- app.use(function(req, res, next) {
- var err = new Error('Not Found');
- err.status = 404;
- next(err);
- });
- // error handlers
- ```
- We have done what we need to do with app.js for the moment so lets create some routes.
- #### Creating Routes
- Lets go into routes directory and create a new file called passport.js
- ```bash
- cd routes && touch passport.js
- ```
- We are going to define 6 routes, the home page, the login page, a sign up page, a profile page, and the two post routes to handle login and sign up and we will do this in the file we just created.
- ```js
- // routes/passport.js
- module.exports = function(app, passport) {
- // =====================================
- // HOME PAGE (with login links) ========
- // =====================================
- app.get('/', function(req, res) {
- res.render('index.ejs'); // load the index.ejs file
- });
- // =====================================
- // LOGIN ===============================
- // =====================================
- // show the login form
- app.get('/login', function(req, res) {
- // render the page and pass in any flash data if it exists
- res.render('login.ejs', { message: req.flash('loginMessage') });
- });
- // process the login form
- // app.post('/login', do all our passport stuff here);
- // =====================================
- // SIGNUP ==============================
- // =====================================
- // show the signup form
- app.get('/signup', function(req, res) {
- // render the page and pass in any flash data if it exists
- res.render('signup.ejs', { message: req.flash('signupMessage') });
- });
- // process the signup form
- // app.post('/signup', do all our passport stuff here);
- // =====================================
- // PROFILE SECTION =====================
- // =====================================
- // we will want this protected so you have to be logged in to visit
- // we will use route middleware to verify this (the isLoggedIn function)
- app.get('/profile', isLoggedIn, function(req, res) {
- res.render('profile.ejs', {
- user : req.user // get the user out of session and pass to template
- });
- });
- // =====================================
- // LOGOUT ==============================
- // =====================================
- app.get('/logout', function(req, res) {
- req.logout();
- res.redirect('/');
- });
- };
- // route middleware to make sure a user is logged in
- function isLoggedIn(req, res, next) {
- // if user is authenticated in the session, carry on
- if (req.isAuthenticated())
- return next();
- // if they aren't redirect them to the home page
- res.redirect('/');
- }
- ```
- Now lets create the corresponding views:
- ***Note make sure you are in the proper directory when creating these files***
- ```bash
- touch index.ejs login.ejs signup.ejs
- ```
- For now we will just fill in the data with these snippets for the following views:
- Index.ejs:
- ```js
- <!-- views/index.ejs -->
- <!doctype html>
- <html>
- <head>
- <title>Node Authentication</title>
- <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"> <!-- load bootstrap css -->
- <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
- <style>
- body { padding-top:80px; }
- </style>
- </head>
- <body>
- <div class="container">
- <div class="jumbotron text-center">
- <h1><span class="fa fa-lock"></span> Node Authentication</h1>
- <p>Login or Register with:</p>
- <a href="/login" class="btn btn-default"><span class="fa fa-user"></span> Local Login</a>
- <a href="/signup" class="btn btn-default"><span class="fa fa-user"></span> Local Signup</a>
- </div>
- </div>
- </body>
- </html>
- ```
- Login.ejs
- ```js
- <!-- views/login.ejs -->
- <!doctype html>
- <html>
- <head>
- <title>Node Authentication</title>
- <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"> <!-- load bootstrap css -->
- <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
- <style>
- body { padding-top:80px; }
- </style>
- </head>
- <body>
- <div class="container">
- <div class="col-sm-6 col-sm-offset-3">
- <h1><span class="fa fa-sign-in"></span> Login</h1>
- <!-- show any messages that come back with authentication -->
- <% if (message.length > 0) { %>
- <div class="alert alert-danger"><%= message %></div>
- <% } %>
- <!-- LOGIN FORM -->
- <form action="/login" method="post">
- <div class="form-group">
- <label>Email</label>
- <input type="text" class="form-control" name="email">
- </div>
- <div class="form-group">
- <label>Password</label>
- <input type="password" class="form-control" name="password">
- </div>
- <button type="submit" class="btn btn-warning btn-lg">Login</button>
- </form>
- <hr>
- <p>Need an account? <a href="/signup">Signup</a></p>
- <p>Or go <a href="/">home</a>.</p>
- </div>
- </div>
- </body>
- </html>
- ```
- Signup.ejs
- ```js
- <!-- views/signup.ejs -->
- <!doctype html>
- <html>
- <head>
- <title>Node Authentication</title>
- <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"> <!-- load bootstrap css -->
- <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
- <style>
- body { padding-top:80px; }
- </style>
- </head>
- <body>
- <div class="container">
- <div class="col-sm-6 col-sm-offset-3">
- <h1><span class="fa fa-sign-in"></span> Signup</h1>
- <!-- show any messages that come back with authentication -->
- <% if (message.length > 0) { %>
- <div class="alert alert-danger"><%= message %></div>
- <% } %>
- <!-- LOGIN FORM -->
- <form action="/signup" method="post">
- <div class="form-group">
- <label>Email</label>
- <input type="text" class="form-control" name="email">
- </div>
- <div class="form-group">
- <label>Password</label>
- <input type="password" class="form-control" name="password">
- </div>
- <button type="submit" class="btn btn-warning btn-lg">Signup</button>
- </form>
- <hr>
- <p>Already have an account? <a href="/login">Login</a></p>
- <p>Or go <a href="/">home</a>.</p>
- </div>
- </div>
- </body>
- </html>
- ```
- Now that we have created our routes and corresponding views we will need to create our model, and configure passport to process our login/signup forms.
- #### Creating the models
- Create a new models directory
- ```bash
- mkdir models
- ```
- Create a new user.js file to hold our model **Note make sure you are placing the file in the correct place**
- ```bash
- touch user.js
- ```
- Open user.js and lets put this in there:
- ```js
- // models/user.js
- // load the things we need
- var mongoose = require('mongoose');
- var bcrypt = require('bcrypt-nodejs');
- // define the schema for our user model
- var userSchema = mongoose.Schema({
- local : {
- email : String,
- password : String,
- }
- });
- // methods ======================
- // generating a hash
- userSchema.methods.generateHash = function(password) {
- return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
- };
- // checking if password is valid
- userSchema.methods.validPassword = function(password) {
- return bcrypt.compareSync(password, this.local.password);
- };
- // create the model for users and expose it to our app
- module.exports = mongoose.model('User', userSchema);
- ```
- Now that we have defined our model, lets configure passport for local accounts.
- Lets create a new directory named config in the root of our project
- ```bash
- mkdir config
- ```
- Create a file named passport.js in the config folder
- ```bash
- touch passport.js
- ```
- Open the page up and lets put this snippet into the page and discuss:
- ```js
- // config/passport.js
- // load all the things we need
- var LocalStrategy = require('passport-local').Strategy;
- // load up the user model
- var User = require('../models/user');
- // expose this function to our app using module.exports
- module.exports = function(passport) {
- // =========================================================================
- // passport session setup ==================================================
- // =========================================================================
- // required for persistent login sessions
- // passport needs ability to serialize and unserialize users out of session
- // used to serialize the user for the session
- passport.serializeUser(function(user, done) {
- done(null, user.id);
- });
- // used to deserialize the user
- passport.deserializeUser(function(id, done) {
- User.findById(id, function(err, user) {
- done(err, user);
- });
- });
- // =========================================================================
- // LOCAL SIGNUP ============================================================
- // =========================================================================
- // we are using named strategies since we have one for login and one for signup
- // by default, if there was no name, it would just be called 'local'
- passport.use('local-signup', new LocalStrategy({
- // by default, local strategy uses username and password, we will override with email
- usernameField : 'email',
- passwordField : 'password',
- passReqToCallback : true // allows us to pass back the entire request to the callback
- },
- function(req, email, password, done) {
- // asynchronous
- // User.findOne wont fire unless data is sent back
- process.nextTick(function() {
- // find a user whose email is the same as the forms email
- // we are checking to see if the user trying to login already exists
- User.findOne({ 'local.email' : email }, function(err, user) {
- // if there are any errors, return the error
- if (err)
- return done(err);
- // check to see if theres already a user with that email
- if (user) {
- return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
- } else {
- // if there is no user with that email
- // create the user
- var newUser = new User();
- // set the user's local credentials
- newUser.local.email = email;
- newUser.local.password = newUser.generateHash(password);
- // save the user
- newUser.save(function(err) {
- if (err)
- throw err;
- return done(null, newUser);
- });
- }
- });
- });
- }));
- };
- ```
- We have now provided a strategy to passport called local-signup. We will use this strategy to process our signup form. Let’s open up our routes/passport.js and handle the POST for our signup form.
- ```js
- // process the signup form
- app.post('/signup', passport.authenticate('local-signup', {
- successRedirect : '/profile', // redirect to the secure profile section
- failureRedirect : '/signup', // redirect back to the signup page if there is an error
- failureFlash : true // allow flash messages
- }));
- // app.post('/signup', do all our passport stuff here);
- ```
- Lets add this line in our app.js file underneath our database connection:
- ```js
- // Mongoose connection
- var mongoose = require('mongoose');
- mongoose.connect(process.env.DB_CONN_MEET);
- // pass passport for configuration
- require('./config/passport')(passport);
- app.use('/', routes);
- app.use('/users', users);
- ```
- We can now test to see if our sign up and database are working. Test out your app and and create a new user. Then go to mongolab and see if that user has been created. If so we need them to be able to log-in so we need to add that strategy to our config/passport.js file.
- Now lets add the login strategy to our config/passport.js file:
- ```js
- // config/passport.js
- // load all the things we need
- var LocalStrategy = require('passport-local').Strategy;
- // load up the user model
- var User = require('../models/user');
- // expose this function to our app using module.exports
- module.exports = function(passport) {
- // =========================================================================
- // passport session setup ==================================================
- // =========================================================================
- // required for persistent login sessions
- // passport needs ability to serialize and unserialize users out of session
- // used to serialize the user for the session
- passport.serializeUser(function(user, done) {
- done(null, user.id);
- });
- // used to deserialize the user
- passport.deserializeUser(function(id, done) {
- User.findById(id, function(err, user) {
- done(err, user);
- });
- });
- // =========================================================================
- // LOCAL SIGNUP ============================================================
- // =========================================================================
- // we are using named strategies since we have one for login and one for signup
- // by default, if there was no name, it would just be called 'local'
- passport.use('local-signup', new LocalStrategy({
- // by default, local strategy uses username and password, we will override with email
- usernameField : 'email',
- passwordField : 'password',
- passReqToCallback : true // allows us to pass back the entire request to the callback
- },
- function(req, email, password, done) {
- // asynchronous
- // User.findOne wont fire unless data is sent back
- process.nextTick(function() {
- // find a user whose email is the same as the forms email
- // we are checking to see if the user trying to login already exists
- User.findOne({ 'local.email' : email }, function(err, user) {
- // if there are any errors, return the error
- if (err)
- return done(err);
- // check to see if theres already a user with that email
- if (user) {
- return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
- } else {
- // if there is no user with that email
- // create the user
- var newUser = new User();
- // set the user's local credentials
- newUser.local.email = email;
- newUser.local.password = newUser.generateHash(password);
- // save the user
- newUser.save(function(err) {
- if (err)
- throw err;
- return done(null, newUser);
- });
- }
- });
- });
- }));
- // =========================================================================
- // LOCAL LOGIN =============================================================
- // =========================================================================
- // we are using named strategies since we have one for login and one for signup
- // by default, if there was no name, it would just be called 'local'
- passport.use('local-login', new LocalStrategy({
- // by default, local strategy uses username and password, we will override with email
- usernameField : 'email',
- passwordField : 'password',
- passReqToCallback : true // allows us to pass back the entire request to the callback
- },
- function(req, email, password, done) { // callback with email and password from our form
- // find a user whose email is the same as the forms email
- // we are checking to see if the user trying to login already exists
- User.findOne({ 'local.email' : email }, function(err, user) {
- // if there are any errors, return the error before anything else
- if (err)
- return done(err);
- // if no user is found, return the message
- if (!user)
- return done(null, false, req.flash('loginMessage', 'No user found.')); // req.flash is the way to set flashdata using connect-flash
- // if the user is found but the password is wrong
- if (!user.validPassword(password))
- return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata
- // all is well, return successful user
- return done(null, user);
- });
- }));
- };
- ```
- We have now provided a strategy to passport called local-login. We will use this strategy to process our login form. We can check if a user exists, if the password is wrong, and set flash data to show error messages. Let’s open up our routes/passport.js and handle the POST for our login form.
- ```js
- // routes/passport.js
- ...
- // process the login form
- app.post('/login', passport.authenticate('local-login', {
- successRedirect : '/profile', // redirect to the secure profile section
- failureRedirect : '/login', // redirect back to the signup page if there is an error
- failureFlash : true // allow flash messages
- }));
- ...
- ```
- Lets add a profile page to views
- ```bash
- touch profile.ejs
- ```
- Add this as our view:
- ```js
- <!-- views/profile.ejs -->
- <!doctype html>
- <html>
- <head>
- <title>Node Authentication</title>
- <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css">
- <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css">
- <style>
- body { padding-top:80px; word-wrap:break-word; }
- </style>
- </head>
- <body>
- <div class="container">
- <div class="page-header text-center">
- <h1><span class="fa fa-anchor"></span> Profile Page</h1>
- <a href="/logout" class="btn btn-default btn-sm">Logout</a>
- </div>
- <div class="row">
- <!-- LOCAL INFORMATION -->
- <div class="col-sm-6">
- <div class="well">
- <h3><span class="fa fa-user"></span> Local</h3>
- <p>
- <strong>id</strong>: <%= user._id %><br>
- <strong>email</strong>: <%= user.local.email %><br>
- <strong>password</strong>: <%= user.local.password %>
- </p>
- </div>
- </div>
- </div>
- </div>
- </body>
- </html>
- ```
- We have now created a basic User Authenticated page using passport.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement