[Login & Register authentication with Node js] (8) ๋ก๊ทธ์ธ ์ auth ์ธ์ฆ ํ, dashboard ๋์ฐ๊ธฐ by passport
<๋ณธ ๋ธ๋ก๊ทธ๋ Traversy Media ์ ์ ํ๋ธ๋ฅผ ์ฐธ๊ณ ํด์ ๊ณต๋ถํ๋ฉฐ ์์ฑํ์์ต๋๋ค :-)>
=> Node.js With Passport Authentication | Full Project
=> authentication app with login, register and access control using Node.js, Express, Passport, Mongoose
๐ฅ ๋ก๊ทธ์ธ์ ๋์์ฃผ๋ passpoart
์ด๋ฆ์ฒ๋ผ ์์ ์ ์น์ฌ์ดํธ์ ๋ฐฉ๋ฌธํ ๋ ์ฌ๊ถ๊ฐ์ ์ญํ
๋ก๊ทธ์ธ์ ์ฝ๊ฒ ํ ์ ์๊ฒ ๋์์ค
// login์ด ์ต์ด๋ก ์ฑ๊ณตํ์ ๋๋ง ํธ์ถ๋๋ ํจ์
// done(null, user.id)๋ก ์ธ์
์ ์ด๊ธฐํ
passport.serializeUser(function (user, cb) {
process.nextTick(function () {
return cb(null, user);
});
});
// ์ฌ์ฉ์๊ฐ ํ์ด์ง๋ฅผ ๋ฐฉ๋ฌธํ ๋๋ง๋ค ํธ์ถ๋๋ ํจ์
// done(null, id)๋ก ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ๊ฐ request์ user ๋ณ์์ ๋ฃ์
passport.deserializeUser(function (user, cb) {
process.nextTick(function () {
return cb(null, user);
});
});
serializeUser : function(user, cb) ์ ์ด์ฉํด์ session์ ์ ์ฅํ ์ ๋ณด cb(null, user) ๋ฅผ ๋๊น
(์๋ ์ฌ์ฉ์ ์ ๋ณด๊ฐ ํฌ๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋ง์ด ์๋ชจํด์, ์ฌ์ฉ์ id์ ๊ฐ์ ํค ์ ๋ณด๋ง ์ ์ฅํ๋๋ฐ ๋ค์์ name์ด ํ์ํ๋ฐ id๋ง ๋์ด์์ ์ฉ ์์์ด ์ฌ์ฉ์ ์ ๋ณด ๋ชจ๋ ์ ์ฅํจ)
LocalStrategy : ๊ฐ์ฒด์ ์ธ์ฆํจ์์์ cb(null, user) ์ ์ํด ๋ฆฌํด๋ ๊ฐ์ด ๋์ด ์ด
deserializeUser : node.js์ ๋ชจ๋ ํ์ด์ง์ ์ ๊ทผํ ๋, ๋ก๊ทธ์ธ์ด ๋์ด ์์ ๊ฒฝ์ฐ ๋ชจ๋ ์ฌ์ฉ์ ํ์ด์ง๋ฅผ ์ ๊ทผํ ๊ฒฝ์ฐ, deserilizeUser๊ฐ ๋ฐ์
session์ ์ ์ฅ๋ ๊ฐ์ ์ด์ฉํด์ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ฐพ์ ํ HTTP request์ ๋ฆฌํด
์ค์ ์๋ฒ๋ก ๋ค์ด์ค๋ ์์ฒญ๋ง๋ค ์ธ์
์ ๋ณด(serializeUser์์ ์ ์ฅ๋จ)๋ฅผ ์ค์ DB์ ๋ฐ์ดํฐ์ ๋น๊ต
๐ง middlewares/passport.js
// ์ธ์ฆ ์์ฒญ์ ์ฒ๋ฆฌํด์ฃผ๋ Node.js์ ์ธ์ฆ ๋ฏธ๋ค์จ์ด
const LocalStrategy = require("passport-local").Strategy;
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
// Load user model
const User = require("../models/User");
module.exports = (passport) => {
passport.use(
new LocalStrategy({ usernameField: "email" }, (email, password, done) => {
// Match User
User.findOne({ email: email })
.then(user => {
// user ๊ฐ ์๋ค๋ฉด done
if (!user) {
return done(null, false, { message: "That email is not registered!" });
}
// Match password (๊ธฐ์กด ๋น๋ฐ๋ฒํธ์ ์
๋ ฅํ ๋น๋ฐ๋ฒํธ ์ฒดํฌ)
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: "Password is incorrect!" });
}
});
})
.catch(err => console.log(err));
})
);
// login์ด ์ต์ด๋ก ์ฑ๊ณตํ์ ๋๋ง ํธ์ถ๋๋ ํจ์
// done(null, user.id)๋ก ์ธ์
์ ์ด๊ธฐํ
passport.serializeUser(function (user, cb) {
process.nextTick(function () {
return cb(null, user);
});
});
// ์ฌ์ฉ์๊ฐ ํ์ด์ง๋ฅผ ๋ฐฉ๋ฌธํ ๋๋ง๋ค ํธ์ถ๋๋ ํจ์
// done(null, id)๋ก ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ๊ฐ request์ user ๋ณ์์ ๋ฃ์
passport.deserializeUser(function (user, cb) {
process.nextTick(function () {
return cb(null, user);
});
});
}
๐ง middlewares/auth.js
module.exports = {
ensureAuthenticated: (req, res, next) => {
if (req.isAuthenticated()) {
return next();
}
req.flash("error_msg", "Plz, log in to view this resource!");
res.redirect("/users/login");
}
}
๐ง routes/index.js
const express = require("express");
const router = express.Router();
const { ensureAuthenticated } = require("../middlewares/auth");
// Main page
router.get("/", (req, res) => {
// rednering ํ ๋ view์ ํ์ผ ์ด๋ฆ ์จ์ผ๋จ
res.render("welcome");
});
// dashboard
router.get("/dashboard", ensureAuthenticated, (req, res) => {
// rendering ํ ๋ view์ ํ์ผ ์ด๋ฆ ์จ์ผ๋จ
res.render("dashboard", {
name : req.user.name
});
});
module.exports = router;
๐ง routes/user.js
const express = require("express");
const router = express.Router();
const bcrypt = require("bcryptjs");
const passport = require("passport");
// user model
const User = require("../models/User");
// Login Page
router.get("/login", (req, res) => {
res.render("login");
});
// Register Page
router.get("/register", (req, res) => {
res.render("register");
});
// Register Handle
router.post('/register', (req, res) => {
const { name, email, password, password2 } = req.body;
let errors = [];
if (!name || !email || !password || !password2) {
errors.push({ msg: 'Please enter all fields' });
}
if (password != password2) {
errors.push({ msg: 'Passwords do not match' });
}
if (password.length < 6) {
errors.push({ msg: 'Password must be at least 6 characters' });
}
if (errors.length > 0) {
res.render('register', {
errors,
name,
email,
password,
password2
});
} else {
// validation pass
User.findOne({ email: email })
.then(user => {
// email์ ์ฐพ์๋๋ฐ ๋ง์ฝ์ user๊ฐ ์๋ค๋ฉด
if (user) {
errors.push({ msg: "Email is already registered!" });
res.render('register', {
errors,
name,
email,
password,
password2
});
} else {
// ํ์๊ฐ์
๋ email์ด ์๋ค๋ฉด
const newUser = new User({
name,
email,
password
});
console.log(newUser);
// hash password
bcrypt.genSalt(10, (err, salt) =>
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
// Set password to hased
newUser.password = hash;
// save user
newUser.save()
.then(user => {
req.flash("success_msg", "You are now registered and can log in!!");
res.redirect('/users/login');
})
.catch(err => console.log(err));
}))
}
});
}
});
// Login handle
router.post("/login", (req, res, next) => {
passport.authenticate("local", {
// ์ฑ๊ณตํ๋ฉด ๋ฉ์ธ์ผ๋ก
successRedirect : "/dashboard",
// ์คํจํ๋ฉด ๋ค์ ๋ก๊ทธ์ธ
failureRedirect : "/users/login",
failureFlash : true
}) (req, res, next);
});
// Logout handle
router.get('/logout', (req, res, next) => {
req.logOut(err => {
if (err) {
return next(err);
} else {
console.log('๋ก๊ทธ์์๋จ.');
req.flash("success-msg", "You are logged out!");
res.redirect("/users/login");
}
});
});
module.exports = router;
๐ง app.js
const express = require("express");
const expressLayouts = require("express-ejs-layouts");
const mongoose = require("mongoose");
const flash = require("connect-flash");
const session = require("express-session");
const passport = require("passport");
const app = express();
require("dotenv").config();
require("./middlewares/passport")(passport);
// DB config
const db = process.env.MONGODB_URI;
// connect to Mongo
mongoose.connect(db, {
useNewUrlParser: true, // useNewUrlParser : ์๋ฌ ๋ฐฉ์ง
useUnifiedTopology: true
})
.then(() => console.log("๐MongoDB Connected..."))
.catch(err => console.log(err));
// ejs ๋ฏธ๋ค์จ์ด
app.use(expressLayouts);
// express ์ view ์์ง์ ejs ๋ก ์ธํ
app.set("view engine", "ejs");
// Bodyparser
// express์๋ฒ๋ก POST์์ฒญ์ ํ ๋ inputํ๊ทธ์ value๋ฅผ ์ ๋ฌ
// URL-encoded ํ์์ ๋ฌธ์์ด๋ก ๋์ด์ค๊ธฐ ๋๋ฌธ์ ๊ฐ์ฒด๋ก์ ๋ณํ ํ์
app.use(express.urlencoded({ extended: false }));
// Express session
// Express ํ๋ ์์ํฌ์์ ์ธ์
์ ๊ด๋ฆฌํ๊ธฐ ์ํ ๋ฏธ๋ค์จ์ด
// Server์์ Client์๊ฒ ์ฟ ํค๋ก sessionID๋ฅผ ๋ฐ๊ธํด์ฃผ๊ณ ,
// ์ด ์ฟ ํค๋ฅผ ํตํด Server์ ์ ์ํ๋ฉด sessionID๊ฐ์ ํ์ฉํด ์ด๋ค Client์ธ์ง ์๋ณํ๊ณ ๊ด๋ จ ์ ๋ณด๋ฅผ ์ ๊ณต
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true,
}));
// passport ์ด๊ธฐํ ๋ฐ session ์ฐ๊ฒฐ ๋ฏธ๋ค์จ์ด
app.use(passport.authenticate('session'));
app.use(passport.initialize());
app.use(passport.session());
// Connect flash
// ํ ๋ฒ ์ถ๋ ฅ๋๊ณ ์ฌ๋ผ์ง๋ ๋ฉ์์ง
// session ๋ณด๋ค ์๋์ชฝ์์ ๋ฏธ๋ค์จ์ด๋ฅผ ์ค์น
// flash ๋ฉ์์ง๋ Queue์ฒ๋ผ ๋ฐ์ดํฐ๋ฅผ ๊บผ๋ด๋ ์ฆ์, session์์ ์ญ์
app.use(flash());
// Global Vars
app.use((req, res, next) => {
// res.locals : ๋ทฐ๋ฅผ ๋ ๋๋งํ๋ ๊ธฐ๋ณธ ์ฝํ
์คํธ๋ฅผ ํฌํจํ๋ ๊ฐ์ฒด
res.locals.success_msg = req.flash("success_msg");
res.locals.error_msg = req.flash("error_msg");
res.locals.error = req.flash("error");
next();
});
// uploads ํด๋์ ์ฌ์ง html์ ๋ณด์ด๊ฒ ํ๊ธฐg
app.use(express.static('public'));
// Routes
app.use("/", require("./routes/index"));
app.use("/users", require("./routes/user"));
const PORT = process.env.PORT || 8000;
app.listen(PORT, console.log(`๐Server started on port http://localhost:${PORT}`));
๐ง views/dashboard.ejs
<h1 class="mt-4">Dashboard</h1>
<p class="lead mb-3">Welcome User! <%= name %></p>
<a href="/users/logout" class="btn btn-secondary">Logout</a>