Advertisement
Guest User

Untitled

a guest
Apr 20th, 2016
111
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 22.01 KB | None | 0 0
  1. ## User Authentication
  2.  
  3.  
  4.  
  5. ### Authentication with Passport
  6.  
  7. #### Beginning a new project
  8.  
  9. Create a new directory named userauth and cd into that directory
  10.  
  11. ```bash
  12. mkdir userauth && cd userauth
  13. ```
  14. Create the express application
  15.  
  16. ```bash
  17. express -e
  18. ```
  19. Install the dependencies
  20.  
  21. ```bash
  22. npm install
  23. ```
  24.  
  25. ***Note at this point in time it would be wise to set up version control with git and github if you haven't already***
  26.  
  27. #### Setting up the database
  28.  
  29. Navigate to mongolab and lets get our database setup.
  30.  
  31. In mongo lab we need to:
  32.  
  33. 1. Create a new database
  34. 2. Create the database user
  35. 3. Get our Standard URI connection string to add to our bash profile.
  36. ***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***
  37.  
  38. Now we need to add our connection string with the appropriate username and password to our bash profile:
  39.  
  40. ```bash
  41. subl ~/.bash_profile
  42. ```
  43.  
  44. Add the connection string to the appropriate place in your bash profile in the following format:
  45.  
  46. export DB_CONN_USERAUTH="mongodb://matt:matt@ds055515.mongolab.com:55515/userauth"
  47.  
  48. ***DON'T FORGET THIS STEP... SOURCE YOUR BASH PROFILE OR RESTART THE TERMINAL APPLICATION COMPLETELY***
  49.  
  50. ```bash
  51. source ~/.bash_profile
  52. ```
  53. Now lets install the mongoose package:
  54.  
  55. ```bash
  56. npm install mongoose --save
  57. ```
  58.  
  59. Lets add our connection to our app.js file now that we have all of the pieces:
  60.  
  61. ```
  62. // app.js
  63. // ...
  64. app.use(logger('dev'));
  65. app.use(bodyParser.json());
  66. app.use(bodyParser.urlencoded({ extended: false }));
  67. app.use(cookieParser());
  68. app.use(express.static(path.join(__dirname, 'public')));
  69.  
  70. // Mongoose connection
  71. var mongoose = require('mongoose');
  72. mongoose.connect(process.env.DB_CONN_USERAUTH);
  73.  
  74. app.use('/', routes);
  75. app.use('/users', users);
  76. // ...
  77. ```
  78.  
  79. Now that we have our app set up for our database lets get down to authenticating our users:
  80.  
  81.  
  82. #### Installing all of our dependencies
  83.  
  84. We are going to have a few dependencies we need to install so lets do that first.
  85.  
  86. We need to add passport, passport-local, bcrypt, connect flash, and express session.
  87.  
  88. ```bash
  89. npm install passport passport-local bcrypt-nodejs connect-flash express-session --save
  90. ```
  91.  
  92. 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**
  93.  
  94. ```js
  95. var express = require('express');
  96. var path = require('path');
  97. var favicon = require('serve-favicon');
  98. var logger = require('morgan');
  99. var cookieParser = require('cookie-parser');
  100. var bodyParser = require('body-parser');
  101. // Add passport
  102. var passport = require('passport');
  103. // Add connect flash
  104. var flash = require('connect-flash');
  105. // Add express session
  106. var session = require('express-session');
  107.  
  108.  
  109. var routes = require('./routes/index');
  110. var users = require('./routes/users');
  111.  
  112. var app = express();
  113. ```
  114.  
  115. Further, for passport we need to also add these lines into app.js file.
  116.  
  117. ```js
  118. app.use('/', routes);
  119. app.use('/users', users);
  120.  
  121. // Required for passport
  122. app.use(session({ secret: 'b1t3myshlnym3t@L@5s' })); // session secret
  123. app.use(passport.initialize()); //initialize passport
  124. app.use(passport.session()); // persistent login sessions
  125. app.use(flash()); // use connect-flash for flash messages stored in session
  126.  
  127. // routes
  128. require('./routes/passport.js')(app, passport); // load our routes and pass in our app
  129.  
  130.  
  131.  
  132. // catch 404 and forward to error handler
  133. app.use(function(req, res, next) {
  134. var err = new Error('Not Found');
  135. err.status = 404;
  136. next(err);
  137. });
  138.  
  139. // error handlers
  140. ```
  141.  
  142. We have done what we need to do with app.js for the moment so lets create some routes.
  143.  
  144. #### Creating Routes
  145.  
  146. Lets go into routes directory and create a new file called passport.js
  147.  
  148. ```bash
  149. cd routes && touch passport.js
  150. ```
  151.  
  152. 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.
  153.  
  154. ```js
  155. // routes/passport.js
  156. module.exports = function(app, passport) {
  157.  
  158. // =====================================
  159. // HOME PAGE (with login links) ========
  160. // =====================================
  161. app.get('/', function(req, res) {
  162. res.render('index.ejs'); // load the index.ejs file
  163. });
  164.  
  165. // =====================================
  166. // LOGIN ===============================
  167. // =====================================
  168. // show the login form
  169. app.get('/login', function(req, res) {
  170.  
  171. // render the page and pass in any flash data if it exists
  172. res.render('login.ejs', { message: req.flash('loginMessage') });
  173. });
  174.  
  175. // process the login form
  176. // app.post('/login', do all our passport stuff here);
  177.  
  178. // =====================================
  179. // SIGNUP ==============================
  180. // =====================================
  181. // show the signup form
  182. app.get('/signup', function(req, res) {
  183.  
  184. // render the page and pass in any flash data if it exists
  185. res.render('signup.ejs', { message: req.flash('signupMessage') });
  186. });
  187.  
  188. // process the signup form
  189. // app.post('/signup', do all our passport stuff here);
  190.  
  191. // =====================================
  192. // PROFILE SECTION =====================
  193. // =====================================
  194. // we will want this protected so you have to be logged in to visit
  195. // we will use route middleware to verify this (the isLoggedIn function)
  196. app.get('/profile', isLoggedIn, function(req, res) {
  197. res.render('profile.ejs', {
  198. user : req.user // get the user out of session and pass to template
  199. });
  200. });
  201.  
  202. // =====================================
  203. // LOGOUT ==============================
  204. // =====================================
  205. app.get('/logout', function(req, res) {
  206. req.logout();
  207. res.redirect('/');
  208. });
  209. };
  210.  
  211. // route middleware to make sure a user is logged in
  212. function isLoggedIn(req, res, next) {
  213.  
  214. // if user is authenticated in the session, carry on
  215. if (req.isAuthenticated())
  216. return next();
  217.  
  218. // if they aren't redirect them to the home page
  219. res.redirect('/');
  220. }
  221. ```
  222.  
  223. Now lets create the corresponding views:
  224. ***Note make sure you are in the proper directory when creating these files***
  225.  
  226. ```bash
  227. touch index.ejs login.ejs signup.ejs
  228. ```
  229.  
  230. For now we will just fill in the data with these snippets for the following views:
  231.  
  232. Index.ejs:
  233. ```js
  234. <!-- views/index.ejs -->
  235. <!doctype html>
  236. <html>
  237. <head>
  238. <title>Node Authentication</title>
  239. <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"> <!-- load bootstrap css -->
  240. <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
  241. <style>
  242. body { padding-top:80px; }
  243. </style>
  244. </head>
  245. <body>
  246. <div class="container">
  247.  
  248. <div class="jumbotron text-center">
  249. <h1><span class="fa fa-lock"></span> Node Authentication</h1>
  250.  
  251. <p>Login or Register with:</p>
  252.  
  253. <a href="/login" class="btn btn-default"><span class="fa fa-user"></span> Local Login</a>
  254. <a href="/signup" class="btn btn-default"><span class="fa fa-user"></span> Local Signup</a>
  255. </div>
  256.  
  257. </div>
  258. </body>
  259. </html>
  260. ```
  261.  
  262. Login.ejs
  263. ```js
  264. <!-- views/login.ejs -->
  265. <!doctype html>
  266. <html>
  267. <head>
  268. <title>Node Authentication</title>
  269. <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"> <!-- load bootstrap css -->
  270. <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
  271. <style>
  272. body { padding-top:80px; }
  273. </style>
  274. </head>
  275. <body>
  276. <div class="container">
  277.  
  278. <div class="col-sm-6 col-sm-offset-3">
  279.  
  280. <h1><span class="fa fa-sign-in"></span> Login</h1>
  281.  
  282. <!-- show any messages that come back with authentication -->
  283. <% if (message.length > 0) { %>
  284. <div class="alert alert-danger"><%= message %></div>
  285. <% } %>
  286.  
  287. <!-- LOGIN FORM -->
  288. <form action="/login" method="post">
  289. <div class="form-group">
  290. <label>Email</label>
  291. <input type="text" class="form-control" name="email">
  292. </div>
  293. <div class="form-group">
  294. <label>Password</label>
  295. <input type="password" class="form-control" name="password">
  296. </div>
  297.  
  298. <button type="submit" class="btn btn-warning btn-lg">Login</button>
  299. </form>
  300.  
  301. <hr>
  302.  
  303. <p>Need an account? <a href="/signup">Signup</a></p>
  304. <p>Or go <a href="/">home</a>.</p>
  305.  
  306. </div>
  307.  
  308. </div>
  309. </body>
  310. </html>
  311. ```
  312. Signup.ejs
  313.  
  314. ```js
  315. <!-- views/signup.ejs -->
  316. <!doctype html>
  317. <html>
  318. <head>
  319. <title>Node Authentication</title>
  320. <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"> <!-- load bootstrap css -->
  321. <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
  322. <style>
  323. body { padding-top:80px; }
  324. </style>
  325. </head>
  326. <body>
  327. <div class="container">
  328.  
  329. <div class="col-sm-6 col-sm-offset-3">
  330.  
  331. <h1><span class="fa fa-sign-in"></span> Signup</h1>
  332.  
  333. <!-- show any messages that come back with authentication -->
  334. <% if (message.length > 0) { %>
  335. <div class="alert alert-danger"><%= message %></div>
  336. <% } %>
  337.  
  338. <!-- LOGIN FORM -->
  339. <form action="/signup" method="post">
  340. <div class="form-group">
  341. <label>Email</label>
  342. <input type="text" class="form-control" name="email">
  343. </div>
  344. <div class="form-group">
  345. <label>Password</label>
  346. <input type="password" class="form-control" name="password">
  347. </div>
  348.  
  349. <button type="submit" class="btn btn-warning btn-lg">Signup</button>
  350. </form>
  351.  
  352. <hr>
  353.  
  354. <p>Already have an account? <a href="/login">Login</a></p>
  355. <p>Or go <a href="/">home</a>.</p>
  356.  
  357. </div>
  358.  
  359. </div>
  360. </body>
  361. </html>
  362. ```
  363.  
  364. 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.
  365.  
  366. #### Creating the models
  367.  
  368. Create a new models directory
  369.  
  370. ```bash
  371. mkdir models
  372. ```
  373. Create a new user.js file to hold our model **Note make sure you are placing the file in the correct place**
  374.  
  375. ```bash
  376. touch user.js
  377. ```
  378. Open user.js and lets put this in there:
  379.  
  380. ```js
  381. // models/user.js
  382. // load the things we need
  383. var mongoose = require('mongoose');
  384. var bcrypt = require('bcrypt-nodejs');
  385.  
  386. // define the schema for our user model
  387. var userSchema = mongoose.Schema({
  388.  
  389. local : {
  390. email : String,
  391. password : String,
  392. }
  393.  
  394. });
  395.  
  396. // methods ======================
  397. // generating a hash
  398. userSchema.methods.generateHash = function(password) {
  399. return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
  400. };
  401.  
  402. // checking if password is valid
  403. userSchema.methods.validPassword = function(password) {
  404. return bcrypt.compareSync(password, this.local.password);
  405. };
  406.  
  407. // create the model for users and expose it to our app
  408. module.exports = mongoose.model('User', userSchema);
  409. ```
  410. Now that we have defined our model, lets configure passport for local accounts.
  411.  
  412. Lets create a new directory named config in the root of our project
  413.  
  414. ```bash
  415. mkdir config
  416. ```
  417. Create a file named passport.js in the config folder
  418. ```bash
  419. touch passport.js
  420. ```
  421.  
  422. Open the page up and lets put this snippet into the page and discuss:
  423.  
  424. ```js
  425. // config/passport.js
  426.  
  427. // load all the things we need
  428. var LocalStrategy = require('passport-local').Strategy;
  429.  
  430. // load up the user model
  431. var User = require('../models/user');
  432.  
  433. // expose this function to our app using module.exports
  434. module.exports = function(passport) {
  435.  
  436. // =========================================================================
  437. // passport session setup ==================================================
  438. // =========================================================================
  439. // required for persistent login sessions
  440. // passport needs ability to serialize and unserialize users out of session
  441.  
  442. // used to serialize the user for the session
  443. passport.serializeUser(function(user, done) {
  444. done(null, user.id);
  445. });
  446.  
  447. // used to deserialize the user
  448. passport.deserializeUser(function(id, done) {
  449. User.findById(id, function(err, user) {
  450. done(err, user);
  451. });
  452. });
  453.  
  454. // =========================================================================
  455. // LOCAL SIGNUP ============================================================
  456. // =========================================================================
  457. // we are using named strategies since we have one for login and one for signup
  458. // by default, if there was no name, it would just be called 'local'
  459.  
  460. passport.use('local-signup', new LocalStrategy({
  461. // by default, local strategy uses username and password, we will override with email
  462. usernameField : 'email',
  463. passwordField : 'password',
  464. passReqToCallback : true // allows us to pass back the entire request to the callback
  465. },
  466. function(req, email, password, done) {
  467.  
  468. // asynchronous
  469. // User.findOne wont fire unless data is sent back
  470. process.nextTick(function() {
  471.  
  472. // find a user whose email is the same as the forms email
  473. // we are checking to see if the user trying to login already exists
  474. User.findOne({ 'local.email' : email }, function(err, user) {
  475. // if there are any errors, return the error
  476. if (err)
  477. return done(err);
  478.  
  479. // check to see if theres already a user with that email
  480. if (user) {
  481. return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
  482. } else {
  483.  
  484. // if there is no user with that email
  485. // create the user
  486. var newUser = new User();
  487.  
  488. // set the user's local credentials
  489. newUser.local.email = email;
  490. newUser.local.password = newUser.generateHash(password);
  491.  
  492. // save the user
  493. newUser.save(function(err) {
  494. if (err)
  495. throw err;
  496. return done(null, newUser);
  497. });
  498. }
  499.  
  500. });
  501.  
  502. });
  503.  
  504. }));
  505.  
  506. };
  507. ```
  508.  
  509. 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.
  510.  
  511. ```js
  512. // process the signup form
  513. app.post('/signup', passport.authenticate('local-signup', {
  514. successRedirect : '/profile', // redirect to the secure profile section
  515. failureRedirect : '/signup', // redirect back to the signup page if there is an error
  516. failureFlash : true // allow flash messages
  517. }));
  518. // app.post('/signup', do all our passport stuff here);
  519. ```
  520.  
  521. Lets add this line in our app.js file underneath our database connection:
  522. ```js
  523. // Mongoose connection
  524. var mongoose = require('mongoose');
  525. mongoose.connect(process.env.DB_CONN_MEET);
  526.  
  527. // pass passport for configuration
  528. require('./config/passport')(passport);
  529.  
  530.  
  531. app.use('/', routes);
  532. app.use('/users', users);
  533. ```
  534.  
  535. 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.
  536.  
  537. Now lets add the login strategy to our config/passport.js file:
  538.  
  539. ```js
  540. // config/passport.js
  541.  
  542. // load all the things we need
  543. var LocalStrategy = require('passport-local').Strategy;
  544.  
  545. // load up the user model
  546. var User = require('../models/user');
  547.  
  548. // expose this function to our app using module.exports
  549. module.exports = function(passport) {
  550.  
  551. // =========================================================================
  552. // passport session setup ==================================================
  553. // =========================================================================
  554. // required for persistent login sessions
  555. // passport needs ability to serialize and unserialize users out of session
  556.  
  557. // used to serialize the user for the session
  558. passport.serializeUser(function(user, done) {
  559. done(null, user.id);
  560. });
  561.  
  562. // used to deserialize the user
  563. passport.deserializeUser(function(id, done) {
  564. User.findById(id, function(err, user) {
  565. done(err, user);
  566. });
  567. });
  568.  
  569. // =========================================================================
  570. // LOCAL SIGNUP ============================================================
  571. // =========================================================================
  572. // we are using named strategies since we have one for login and one for signup
  573. // by default, if there was no name, it would just be called 'local'
  574.  
  575. passport.use('local-signup', new LocalStrategy({
  576. // by default, local strategy uses username and password, we will override with email
  577. usernameField : 'email',
  578. passwordField : 'password',
  579. passReqToCallback : true // allows us to pass back the entire request to the callback
  580. },
  581. function(req, email, password, done) {
  582.  
  583. // asynchronous
  584. // User.findOne wont fire unless data is sent back
  585. process.nextTick(function() {
  586.  
  587. // find a user whose email is the same as the forms email
  588. // we are checking to see if the user trying to login already exists
  589. User.findOne({ 'local.email' : email }, function(err, user) {
  590. // if there are any errors, return the error
  591. if (err)
  592. return done(err);
  593.  
  594. // check to see if theres already a user with that email
  595. if (user) {
  596. return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
  597. } else {
  598.  
  599. // if there is no user with that email
  600. // create the user
  601. var newUser = new User();
  602.  
  603. // set the user's local credentials
  604. newUser.local.email = email;
  605. newUser.local.password = newUser.generateHash(password);
  606.  
  607. // save the user
  608. newUser.save(function(err) {
  609. if (err)
  610. throw err;
  611. return done(null, newUser);
  612. });
  613. }
  614.  
  615. });
  616.  
  617. });
  618.  
  619. }));
  620.  
  621. // =========================================================================
  622. // LOCAL LOGIN =============================================================
  623. // =========================================================================
  624. // we are using named strategies since we have one for login and one for signup
  625. // by default, if there was no name, it would just be called 'local'
  626.  
  627. passport.use('local-login', new LocalStrategy({
  628. // by default, local strategy uses username and password, we will override with email
  629. usernameField : 'email',
  630. passwordField : 'password',
  631. passReqToCallback : true // allows us to pass back the entire request to the callback
  632. },
  633. function(req, email, password, done) { // callback with email and password from our form
  634.  
  635. // find a user whose email is the same as the forms email
  636. // we are checking to see if the user trying to login already exists
  637. User.findOne({ 'local.email' : email }, function(err, user) {
  638. // if there are any errors, return the error before anything else
  639. if (err)
  640. return done(err);
  641.  
  642. // if no user is found, return the message
  643. if (!user)
  644. return done(null, false, req.flash('loginMessage', 'No user found.')); // req.flash is the way to set flashdata using connect-flash
  645.  
  646. // if the user is found but the password is wrong
  647. if (!user.validPassword(password))
  648. return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata
  649.  
  650. // all is well, return successful user
  651. return done(null, user);
  652. });
  653.  
  654. }));
  655.  
  656. };
  657. ```
  658. 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.
  659. ```js
  660. // routes/passport.js
  661. ...
  662.  
  663. // process the login form
  664. app.post('/login', passport.authenticate('local-login', {
  665. successRedirect : '/profile', // redirect to the secure profile section
  666. failureRedirect : '/login', // redirect back to the signup page if there is an error
  667. failureFlash : true // allow flash messages
  668. }));
  669.  
  670. ...
  671. ```
  672.  
  673. Lets add a profile page to views
  674.  
  675. ```bash
  676. touch profile.ejs
  677. ```
  678.  
  679. Add this as our view:
  680.  
  681. ```js
  682. <!-- views/profile.ejs -->
  683. <!doctype html>
  684. <html>
  685. <head>
  686. <title>Node Authentication</title>
  687. <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css">
  688. <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css">
  689. <style>
  690. body { padding-top:80px; word-wrap:break-word; }
  691. </style>
  692. </head>
  693. <body>
  694. <div class="container">
  695.  
  696. <div class="page-header text-center">
  697. <h1><span class="fa fa-anchor"></span> Profile Page</h1>
  698. <a href="/logout" class="btn btn-default btn-sm">Logout</a>
  699. </div>
  700.  
  701. <div class="row">
  702.  
  703. <!-- LOCAL INFORMATION -->
  704. <div class="col-sm-6">
  705. <div class="well">
  706. <h3><span class="fa fa-user"></span> Local</h3>
  707.  
  708. <p>
  709. <strong>id</strong>: <%= user._id %><br>
  710. <strong>email</strong>: <%= user.local.email %><br>
  711. <strong>password</strong>: <%= user.local.password %>
  712. </p>
  713.  
  714. </div>
  715. </div>
  716.  
  717. </div>
  718.  
  719. </div>
  720. </body>
  721. </html>
  722. ```
  723.  
  724. We have now created a basic User Authenticated page using passport.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement