Skip to content

Instantly share code, notes, and snippets.

@pjchender
Last active October 23, 2019 03:47
Show Gist options
  • Save pjchender/bba7bb7bc819e6997d8a17b2a014c68f to your computer and use it in GitHub Desktop.
Save pjchender/bba7bb7bc819e6997d8a17b2a014c68f to your computer and use it in GitHub Desktop.
Learn to Use Passport(Passport 學習筆記)
// 匯入需要的模組
const express = require('express')
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')
const path = require('path')
const logger = require('morgan')
const mongoose = require('mongoose')
const session = require('express-session')
const passport = require('./middleware/passport')
const MongoStore = require('connect-mongo')(session) // 直接執行並將 session 存進去,logout 後會自動刪除該 document
const dbconfig = require('./db')
// 和 mongoDB 連線
mongoose.connect(dbconfig.connection) // 等同於,mongoose.connect('mongodb://localhost:27017/bookworm')
const db = mongoose.connection
db.on('error', console.error.bind(console, 'connection error')) // mongo error handler
// 載入 express
const app = express()
// 設定 view engine 和模版路徑
app.set('view engine', 'pug')
app.set('views', path.join('./views'))
// middleware
app.use(logger('dev'))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(cookieParser())
app.use(express.static(path.join('./public'))) // 讀取 ./public 中的靜態檔案
// 使用 session 來追蹤使用者
app.use(session({
secret: 'I love NodeJS', // secret: 必要欄位,用來註冊 session ID cookie 的字串。如此將增加安全性,避免他人在瀏覽器中偽造 cookie。
resave: false, // resave: 不論是否 request 的過程中有無變更都重新將 session 儲存在 session store。
saveUninitialized: false, // saveUninitialized: 將 uninitialized session(新的、未被變更過的) 儲存在 session store 中。
store: new MongoStore({
mongooseConnection: db
})
}))
app.use(passport.initialize())
app.use(passport.session())
// 讓 userID 可以在 template 中被存取,名稱為 currentUser
app.use(function (req, res, next) {
res.locals.currentUser = req.session.userId // res.locals 屬性在所有 view 中都可以存取到
next() // 執行下一個 middleware
})
// 載入路由檔
const index = require('./routes/index')
app.use('/', index)
// catch 404 and forward to error handler
app.use(function (req, res, next) {
var err = new Error('File Not Found')
err.status = 404
next(err)
})
// error handler
// define as the last app.use callback
app.use(function (err, req, res, next) {
res.status(err.status || 500)
res.render('error', {
message: err.message,
error: {}
})
})
// listen on port 3000
app.listen(3000, function () {
console.log('Express app listening on port 3000')
})
// ./routes/index
const express = require('express')
const router = express.Router()
const User = require('../models/user')
const mid = require('../middleware') // 將自己寫的 middleware 載入
const passport = require('../middleware/passport')
// POST /login
router.post('/login', function (req, res, next) {
passport.authenticate('login', function (err, user, info){
if (err) return next(err)
if (!user) {
err = new Error('User not found')
res.status(409)
return next(err)
}
req.login(user, function (err) {
if (err) return next(err)
req.session.userId = user._id
return res.redirect('/profile')
})
})(req, res, next)
})
// POST /register
router.post('/register', function (req, res, next) {
passport.authenticate('signup', function (err, user, info) {
if (err) {
err = new Error('Singup Error')
err.status = 409
return next(err)
}
if (!user) {
err = new Error(info)
err.status = 409
return next(err)
}
req.login(user, function (err) {
if (err) return next(err)
console.log('User account created successfully')
req.session.userId = user._id
return res.redirect('/profile')
})
})(req, res, next)
})
const passport = require('passport')
const bcrypt = require('bcrypt') // hashing module
const LocalStrategy = require('passport-local')
const JwtStrategy = require('passport-jwt').Strategy
const ExtractJwt = require('passport-jwt').ExtractJwt
const User = require('../models/user')
const jwtConfig = require('../config/jwt')
/**
* passport.use('驗證策略名稱', '想建立的策略類型')
* passReqToCallback: 讓我們在後面的 callback 中可以使用 req 參數
*/
// Passport Initialization
passport.serializeUser(function (user, done) {
done(null, user._id)
})
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
done(err, user)
})
})
let jwtStrategy = new JwtStrategy({
secreteOrKey: jwtConfig.secret,
jwtFromRequest: ExtractJwt.fromExtractors([
ExtractJwt.versionOneCompatibility({authScheme: 'Bearer'}),
ExtractJwt.fromAuthHeader()
])
}, function (payload, done) {
User.findById(payload.sub, function (err, user) {
if (err) return done(err)
if (!user) return done(null, false, {message: 'Wrong JWT Token'})
if (payload.aud !== user.email) return done(null, false, {message: 'Wrong JWT Token'})
const exp = payload.exp
const nbf = payload.nbf
const curr = ~~(new Date().getTime() / 1000)
if (curr > exp || curr < nbf) {
return done(null, false, 'Token Expired')
}
return done(null, user)
})
})
let loginStrategy = new LocalStrategy({
usernameField: 'email',
passReqToCallback: true
}, function (req, email, password, done) {
User.findOne({ email: email }, function (err, user) {
if (err) {
return done(err)
}
if (!user) {
return done(null, false, 'Username is not exists')
}
let isValidPassword = function (user, password) {
return bcrypt.compareSync(password, user.password)
}
if (!isValidPassword(user, password)) {
return done(null, false, 'Invalid Password')
}
return done(null, user)
})
})
let signupStrategy = new LocalStrategy({
usernameField: 'email',
passReqToCallback: true
}, function (req, email, password, done) {
if (password !== req.body.confirmPassword) {
return done(new Error('Confirmation is not match with password'))
}
// if (password.length < 8) {
// return done(new Error('Password must has 8 characters at least'))
// }
const findOrCreateUser = function () {
User.findOne({ email: email }, function (err, user) {
if (err) {
return done(err)
}
if (user) {
return done(null, false, 'Username Already Exists')
} else {
let newUser = new User()
newUser.email = email
newUser.password = bcrypt.hashSync(password, 10)
newUser.name = req.body.name
newUser.favoriteBook = req.body.favoriteBook
newUser.save(function (err, user) {
if (err) {
throw err
}
return done(null, user)
})
}
})
}
process.nextTick(findOrCreateUser)
})
passport.use('jwt', jwtStrategy)
passport.use('login', loginStrategy)
passport.use('signup', signupStrategy)
module.exports = passport
// ./models/user
const mongoose = require('mongoose') // ODM for Mongo
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
trim: true,
unique: true
},
name: {
type: String,
required: true,
trim: true
},
favoriteBook: {
type: String,
required: true,
trim: true
},
password: {
type: String,
required: true
}
})
var User = mongoose.model('User', UserSchema)
module.exports = User
@pjchender
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment