visit
- app
------ models
---------- user.js <!-- our user model -->
------ routes.js <!-- all the routes for our application -->
- config
------ auth.js <!-- will hold all our client secret keys (facebook, twitter, google) -->
------ database.js <!-- will hold our database connection settings -->
------ passport.js <!-- configuring the strategies for passport -->
- views
------ index.ejs <!-- show our home page with login links -->
------ login.ejs <!-- show our login form -->
------ signup.ejs <!-- show our signup form -->
------ profile.ejs <!-- after a user logs in, they will see their profile -->
- package.json <!-- handle our npm packages -->
- server.js <!-- setup our application -->
"dependencies": {
"bcrypt-nodejs": "0.0.3",
"body-parser": "^1.19.0",
"connect-flash": "^0.1.1",
"cookie-parser": "^1.4.5",
"ejs": "^3.0.1",
"express": "^4.17.1",
"express-session": "^1.17.0",
"method-override": "^3.0.0",
"mongoose": "^5.9.6",
"morgan": "^1.10.0",
"passport": "^0.4.1",
"passport-local": "^1.0.0",
}
npm i
With all of our packages ready to go, let's set up our application in server.js
.var express = require('express');
var app = express();
var port = process.env.PORT || 8000;
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
// connect to our database
mongoose.connect('mongodb://localhost/blog', {
useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true
})
// pass passport for configuration
// set up our express application
app.use(morgan('dev')); // log every request to the console
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser()); // get information from html forms
app.set('view engine', 'ejs'); // set up ejs
// required for passport
app.use(session({ secret: 'ilovenodejs' })); // session secret
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
// routes
require('./app/routes.js')(app, passport);
require('./config/passport')(passport);
app.listen(port);
The path of our passport object is important to note here. We will create it at the very beginning of the file with var passport = require('passport');. Then we pass it into our config/passport.js file for it to be configured. Then we pass it to the app/routes.js file for it to be used in our routes.
Now with this file, we have our application listening on port 8000. All we have to do to start up our server is:
npm i nodemon
"scripts": {
"on": "nodemon server.js",
"start": "node server.js"
}
nodemon
module.exports = function(app, passport) {
// =======================HOME-PAGE ===============================
app.get('/', function(req, res) {
res.render('index.ejs'); // load the index.ejs file
});
// ==========================LOGIN ===============================
app.get('/login', function(req, res) {
res.render('login.ejs', { message: req.flash('loginMessage') });
});
// =====================SIGNUP ==============================
app.get('/signup', function(req, res) {
res.render('signup.ejs',
{ message: req.flash('signupMessage') });
});
//==========================profile =====================
app.get('/profile', isLoggedIn, function(req, res) {
res.render('profile.ejs', {
user : req.user
});
});
// LOGOUT
app.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
// 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
}));
// 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
}));
};
// makes 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('/');
}
<!doctype html>
<html>
<head>
<title>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">
<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>
<!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 required type="text" class="form-control" name="email">
</div>
<div class="form-group">
<label>Password</label>
<input required 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>
<!doctype html>
<html>
<head>
<title>Signup</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 required type="text" class="form-control" name="email">
</div>
<div class="form-group">
<label>Password</label>
<input required 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>
For local accounts, we will be keeping email and password. You can change these fields out to be whatever you want. You can authenticate locally using username and password (passport-local actually uses username by default but we'll change that to email).
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,
},
facebook : {
id : String,
token : String,
name : String,
email : String
},
twitter : {
id : String,
token : String,
displayName : String,
username : String
},
google : {
id : String,
token : String,
email : String,
name : String
}
});
// generating a hash
userSchema.methods.generateHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
// checks 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);
var LocalStrategy = require('passport-local').Strategy;
var User= require('../app/models/user');
module.exports = function(passport) {
//passport serialize and unserialize users out of session
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
// ======================SIGNUP ===========================
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) {
// User.findOne won't fire unless data is sent back
process.nextTick(function() {
// find a user whose email is the same as the forms email
User.findOne({ 'local.email' : email }, function(err, user) {
if (err)
return done(err);
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 ======================================
passport.use('local-login', new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
// find a user whose email is the same as the forms email
User.findOne({ 'local.email' : email }, function(err, user) {
if (err)
return done(err);
if (!user)
return done(null, false, req.flash('loginMessage', 'No user found.')); // req.flash is the way to set flashdata using connect-flash
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);
});
}));
};
So far, we created our passport object in server.js, and then we pass it to our config/passport.js file. This is where we configure our Strategy for local.
This is also the file where we create the serializeUser and deserializeUser functions to store our user in session.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 app/routes.js and handle the POST for our signup form.
// app/routes.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
}));
...
With users able to sign up, let's give them a way to login.We have 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 app/routes.js and handle the POST for our login form.
// app/routes.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
}));
...
<!doctype html>
<html>
<head>
<title>Profile</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">
<!-- 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>