├── public ├── favicon.png ├── img │ ├── bc4.jpeg │ └── screenshot.png ├── style.css └── main.js ├── .gitignore ├── package.json ├── README.md ├── LICENSE ├── views ├── index.ejs ├── login.ejs ├── signup.ejs ├── connect-local.ejs └── profile.ejs ├── app ├── models │ └── user.js └── routes.js └── server.js /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehfcodes/personal-auth/HEAD/public/favicon.png -------------------------------------------------------------------------------- /public/img/bc4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehfcodes/personal-auth/HEAD/public/img/bc4.jpeg -------------------------------------------------------------------------------- /public/img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ehfcodes/personal-auth/HEAD/public/img/screenshot.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | config 17 | -------------------------------------------------------------------------------- /public/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | padding-top:80px; 3 | word-wrap:break-word; 4 | color: green; 5 | } 6 | img{ 7 | height: 200px; 8 | } 9 | main, header{ 10 | text-align: center; 11 | } 12 | .messages{ 13 | overflow-y: scroll; 14 | overflow-x: hidden; 15 | height: 230px; 16 | } 17 | .well{ 18 | overflow: hidden; 19 | height:230px; 20 | } 21 | #username{ 22 | font-weight: bold; 23 | } 24 | ul{ 25 | list-style: none; 26 | } 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-authentication", 3 | "main": "server.js", 4 | "dependencies": { 5 | "express": "~4.14.0", 6 | "ejs": "~2.5.2", 7 | "mongoose": "~4.13.1", 8 | "passport": "~0.3.2", 9 | "passport-local": "~1.0.0", 10 | "passport-facebook": "~2.1.1", 11 | "passport-twitter": "~1.0.4", 12 | "passport-google-oauth": "~1.0.0", 13 | "connect-flash": "~0.1.1", 14 | "bcrypt-nodejs": "latest", 15 | "morgan": "~1.7.0", 16 | "body-parser": "~1.15.2", 17 | "cookie-parser": "~1.4.3", 18 | "method-override": "~2.3.6", 19 | "express-session": "~1.14.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Powerpuff Pals 2 | Poweruff girls forum where you can also watch the full power puff girls movie. 3 | 4 | ![screenshot of login](https://raw.githubusercontent.com/ehcodes/personal-auth/master/public/img/screenshot.png) 5 | 6 | ## Installation 7 | 8 | 1. Clone repo 9 | 2. run `npm install` 10 | 11 | ## Usage 12 | 13 | 1. run `node server.js` 14 | 2. Navigate to `localhost:8080` 15 | 16 | ## Credit 17 | Modified from Scotch.io's auth tutorial 18 | 19 | ## How It's Made: 20 | 21 | **Tech used:** HTML, CSS, jQuery Javascript 22 | 23 | In this fullstack app, passport auth is utilized, and users can log in to discuss the power puff girls and watch the full Powerpuff Girls movie. 24 | 25 | ## Examples: 26 | Take a look at the rest of my portfolio. 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 RC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Powerpuff Pals 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Powerpuff Pals

15 | Buttercup smiling excitedly 16 |

Sugar, spice, and everything nice these were the ingredients chosen to create the perfect little girls but Professor Utonium accidentally added an extra ingredient to the concotion -- Chemical X.

17 | Login 18 | Signup 19 |
20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /app/models/user.js: -------------------------------------------------------------------------------- 1 | // load the things we need 2 | var mongoose = require('mongoose'); 3 | var bcrypt = require('bcrypt-nodejs'); 4 | 5 | // define the schema for our user model 6 | var userSchema = mongoose.Schema({ 7 | 8 | local : { 9 | email : String, 10 | password : String 11 | }, 12 | facebook : { 13 | id : String, 14 | token : String, 15 | name : String, 16 | email : String 17 | }, 18 | twitter : { 19 | id : String, 20 | token : String, 21 | displayName : String, 22 | username : String 23 | }, 24 | google : { 25 | id : String, 26 | token : String, 27 | email : String, 28 | name : String 29 | } 30 | 31 | }); 32 | 33 | // generating a hash 34 | userSchema.methods.generateHash = function(password) { 35 | return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null); 36 | }; 37 | 38 | // checking if password is valid 39 | userSchema.methods.validPassword = function(password) { 40 | return bcrypt.compareSync(password, this.local.password); 41 | }; 42 | 43 | // create the model for users and expose it to our app 44 | module.exports = mongoose.model('User', userSchema); 45 | -------------------------------------------------------------------------------- /views/login.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Powerpuff Pals 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

Login

14 | <% if (message.length > 0) { %> 15 |
<%= message %>
16 | <% } %> 17 | 18 |
19 |
20 | 21 | 22 |
23 |
24 | 25 | 26 |
27 | 28 |
29 |
30 |

Need an account? Signup

31 |

Or go home.

32 |
33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /views/signup.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Powerpuff Pals 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

Signup

14 | <% if (message.length > 0) { %> 15 |
16 | <%= message %> 17 |
18 | <% } %> 19 | 20 |
21 |
22 | 23 | 24 |
25 |
26 | 27 | 28 |
29 | 30 |
31 |
32 |

Already have an account? Login

33 |

Or go home.

34 |
35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /views/connect-local.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Powerpuff Pals 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Add Local Account

15 | <% if (message.length > 0) { %> 16 |
17 | <%= message %> 18 |
19 | <% } %> 20 | 21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 | 29 |
30 | 31 |
32 |
33 |

Go back to profile

34 |
35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | 3 | // set up ====================================================================== 4 | // get all the tools we need 5 | var express = require('express'); 6 | var app = express(); 7 | var port = process.env.PORT || 8080; 8 | var mongoose = require('mongoose'); 9 | var passport = require('passport'); 10 | var flash = require('connect-flash'); 11 | 12 | var morgan = require('morgan'); 13 | var cookieParser = require('cookie-parser'); 14 | var bodyParser = require('body-parser'); 15 | var session = require('express-session'); 16 | 17 | var configDB = require('./config/database.js'); 18 | 19 | var db 20 | 21 | // configuration =============================================================== 22 | mongoose.connect(configDB.url, { useMongoClient: true }, (err, database) => { 23 | if (err) return console.log(err) 24 | db = database 25 | require('./app/routes.js')(app, passport, db); 26 | }); // connect to our database 27 | 28 | 29 | 30 | require('./config/passport')(passport); // pass passport for configuration 31 | 32 | // set up our express application 33 | app.use(morgan('dev')); // log every request to the console 34 | app.use(cookieParser()); // read cookies (needed for auth) 35 | app.use(bodyParser.json()); // get information from html forms 36 | app.use(bodyParser.urlencoded({ extended: true })); 37 | app.use(express.static('public')) 38 | 39 | app.set('view engine', 'ejs'); // set up ejs for templating 40 | 41 | // required for passport 42 | app.use(session({ 43 | secret: 'rcbootcamp2018a', // session secret 44 | resave: true, 45 | saveUninitialized: true 46 | })); 47 | app.use(passport.initialize()); 48 | app.use(passport.session()); // persistent login sessions 49 | app.use(flash()); // use connect-flash for flash messages stored in session 50 | 51 | 52 | // routes ====================================================================== 53 | //require('./app/routes.js')(app, passport, db); // load our routes and pass in our app and fully configured passport 54 | 55 | // launch ====================================================================== 56 | app.listen(port); 57 | console.log('The magic happens on port ' + port); 58 | -------------------------------------------------------------------------------- /public/main.js: -------------------------------------------------------------------------------- 1 | var thumbUp = document.getElementsByClassName("fa-thumbs-up"); 2 | var thumbDown = document.getElementsByClassName("fa-thumbs-down"); 3 | var trash = document.getElementsByClassName("fa-trash"); 4 | 5 | Array.from(thumbUp).forEach(function(element) { 6 | element.addEventListener('click', function(){ 7 | const name = this.parentNode.parentNode.childNodes[1].innerText 8 | const msg = this.parentNode.parentNode.childNodes[3].innerText 9 | const thumbUp = parseFloat(this.parentNode.parentNode.childNodes[5].innerText) 10 | fetch('messages', { 11 | method: 'put', 12 | headers: {'Content-Type': 'application/json'}, 13 | body: JSON.stringify({ 14 | 'name': name, 15 | 'msg': msg, 16 | 'thumbUp':thumbUp 17 | }) 18 | }) 19 | .then(response => { 20 | if (response.ok) return response.json() 21 | }) 22 | .then(data => { 23 | console.log(data) 24 | window.location.reload(true) 25 | }) 26 | }); 27 | }); 28 | 29 | Array.from(thumbDown).forEach(function(element) { 30 | element.addEventListener('click', function(){ 31 | const name = this.parentNode.parentNode.childNodes[1].innerText 32 | const msg = this.parentNode.parentNode.childNodes[3].innerText 33 | const thumbDown = parseFloat(this.parentNode.parentNode.childNodes[5].innerText) 34 | fetch('messagesDown', { 35 | method: 'put', 36 | headers: {'Content-Type': 'application/json'}, 37 | body: JSON.stringify({ 38 | 'name': name, 39 | 'msg': msg, 40 | 'thumbDown':thumbDown 41 | }) 42 | }) 43 | .then(response => { 44 | if (response.ok) return response.json() 45 | }) 46 | .then(data => { 47 | console.log(data) 48 | window.location.reload(true) 49 | }) 50 | }); 51 | }); 52 | 53 | Array.from(trash).forEach(function(element) { 54 | element.addEventListener('click', function(){ 55 | const name = this.parentNode.parentNode.childNodes[1].innerText 56 | const msg = this.parentNode.parentNode.childNodes[3].innerText 57 | fetch('messages', { 58 | method: 'delete', 59 | headers: { 60 | 'Content-Type': 'application/json' 61 | }, 62 | body: JSON.stringify({ 63 | 'name': name, 64 | 'msg': msg 65 | }) 66 | }).then(function (response) { 67 | window.location.reload() 68 | }) 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /views/profile.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Powerpuff Pals 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |

The Powerpuff Girls Movie

15 |
16 |
17 | 18 |
19 |
20 | 24 |
25 |
26 |
27 |

Local Pal

28 | <% if (user.local.email) { %> 29 |

30 | id: <%= user._id %>
31 | email: <%= user.local.email %>
32 | password: <%= user.local.password %> 33 |

34 | Delete Account 35 | <% } else { %> 36 | Connect Local 37 | <% } %> 38 |
39 |
40 |
41 |

Messages

42 |
    43 | <% for(var i=0; i 44 |
  • 45 | <%= messages[i].name %> 46 | <%= messages[i].msg %> 47 | <%= messages[i].thumbUp %> 48 | <%= messages[i].trash %> 49 | 50 | 51 | 52 |
  • 53 | <% } %> 54 |
55 |

Leave a comment

56 | <% if (user.local.email) { %> 57 |
58 | 59 | 60 | 61 |
62 |
63 | <% } %> 64 |
65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /app/routes.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app, passport, db) { 2 | 3 | // normal routes =============================================================== 4 | 5 | // show the home page (will also have our login links) 6 | app.get('/', function(req, res) { 7 | res.render('index.ejs'); 8 | }); 9 | 10 | // PROFILE SECTION ========================= 11 | app.get('/profile', isLoggedIn, function(req, res) { 12 | db.collection('messages').find().toArray((err, result) => { 13 | if (err) return console.log(err) 14 | res.render('profile.ejs', { 15 | user : req.user, 16 | messages: result 17 | }) 18 | }) 19 | }); 20 | 21 | // LOGOUT ============================== 22 | app.get('/logout', function(req, res) { 23 | req.logout(); 24 | res.redirect('/'); 25 | }); 26 | 27 | // message board routes =============================================================== 28 | 29 | app.post('/messages', (req, res) => { 30 | db.collection('messages').save({name: req.body.name, msg: req.body.msg, thumbUp: 0, thumbDown:0}, (err, result) => { 31 | if (err) return console.log(err) 32 | console.log('saved to database') 33 | res.redirect('/profile') 34 | }) 35 | }) 36 | 37 | app.put('/messages', (req, res) => { 38 | db.collection('messages') 39 | .findOneAndUpdate({name: req.body.name, msg: req.body.msg}, { 40 | $set: { 41 | thumbUp:req.body.thumbUp + 1 42 | } 43 | }, { 44 | sort: {_id: -1}, 45 | upsert: true 46 | }, (err, result) => { 47 | if (err) return res.send(err) 48 | res.send(result) 49 | }) 50 | }) 51 | 52 | app.put('/messagesDown', (req, res) => { 53 | db.collection('messages') 54 | .findOneAndUpdate({name: req.body.name, msg: req.body.msg}, { 55 | $set: { 56 | thumbUp:req.body.thumbDown - 1 57 | } 58 | }, { 59 | sort: {_id: -1}, 60 | upsert: true 61 | }, (err, result) => { 62 | if (err) return res.send(err) 63 | res.send(result) 64 | }) 65 | }) 66 | 67 | app.delete('/messages', (req, res) => { 68 | db.collection('messages').findOneAndDelete({name: req.body.name, msg: req.body.msg}, (err, result) => { 69 | if (err) return res.send(500, err) 70 | res.send('Message deleted!') 71 | }) 72 | }) 73 | 74 | // ============================================================================= 75 | // AUTHENTICATE (FIRST LOGIN) ================================================== 76 | // ============================================================================= 77 | 78 | // locally -------------------------------- 79 | // LOGIN =============================== 80 | // show the login form 81 | app.get('/login', function(req, res) { 82 | res.render('login.ejs', { message: req.flash('loginMessage') }); 83 | }); 84 | 85 | // process the login form 86 | app.post('/login', passport.authenticate('local-login', { 87 | successRedirect : '/profile', // redirect to the secure profile section 88 | failureRedirect : '/login', // redirect back to the signup page if there is an error 89 | failureFlash : true // allow flash messages 90 | })); 91 | 92 | // SIGNUP ================================= 93 | // show the signup form 94 | app.get('/signup', function(req, res) { 95 | res.render('signup.ejs', { message: req.flash('signupMessage') }); 96 | }); 97 | 98 | // process the signup form 99 | app.post('/signup', passport.authenticate('local-signup', { 100 | successRedirect : '/profile', // redirect to the secure profile section 101 | failureRedirect : '/signup', // redirect back to the signup page if there is an error 102 | failureFlash : true // allow flash messages 103 | })); 104 | 105 | // ============================================================================= 106 | // UNLINK ACCOUNTS ============================================================= 107 | // ============================================================================= 108 | // used to unlink accounts. for social accounts, just remove the token 109 | // for local account, remove email and password 110 | // user account will stay active in case they want to reconnect in the future 111 | 112 | // local ----------------------------------- 113 | app.get('/unlink/local', isLoggedIn, function(req, res) { 114 | var user = req.user; 115 | user.local.email = undefined; 116 | user.local.password = undefined; 117 | user.save(function(err) { 118 | res.redirect('/profile'); 119 | }); 120 | }); 121 | 122 | }; 123 | 124 | // route middleware to ensure user is logged in 125 | function isLoggedIn(req, res, next) { 126 | if (req.isAuthenticated()) 127 | return next(); 128 | 129 | res.redirect('/'); 130 | } 131 | --------------------------------------------------------------------------------