延續上次的 "signup 篇",接下來我要介紹 login 篇啦 ~
還記得我們當初介紹的 JWT token
嗎 ?JWT token
是屬於 token-authentication
的一種實作。而根據憑證驗證的概念,我們在登入的時候會攜帶一組 token,所以我也會將 JWT token
融入實作中。
以下範例我暫時先忽略非預期的 Error Handling 的問題,這個部分會在之後的文章中講解。
exports.login = async(req, res, next) => {
const { email, password } = req.body;
// 針對使用者沒有輸入 email or password 的部分
if (!email || !password){
// 備註:
// 此處的 AppError 是我們做的一個 Class 他 extends Error Class
return next(new AppError('Please provide your email and pawwsord', 400));
}
// 找尋是否有 user,並選取他的 password
const user = await User.findOne({ email }).select('+password');
// 接著來比對已經加密的密碼 : 我們需要使用一個 function 在 model 中幫我們 compare password (有關此 function 請看下段程式碼區塊 - function passwordIsMatched
const isMatched = await user.passwordIsMatched(password, user.password);
// 注意! 以下為主要的錯誤示範
if(!user)){
return next(new AppError('Incorrect email'), 401);
}
}
function passwordIsMatched:
userSchema.methods.passwordIsMatched = async function(inputPassword, userPassword) {
return await bcrypt.compare(inputPassword, userPassword);
}
// 其他 code 省略
// 比較好的方式是把上述 error 換成比較模糊的 error message
if(!user || !(await user.passwordIsMatched(password, user.password))){
return next(new AppError('Incorrect email or password'), 401);
}
此處就沒有多加說明 Bad Practice,因為差不多需要注意的有在前面提到。
// 把 signtoken 拆出來,拆成一個 function
const signToken = id => {
return jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: process.new.JWT_EXPIREIN});
}
exports.login = async(req, res, next) => {
// 中間省略
const token = signToken(user._id);
res.status(200).json({
status: 'success',
token,
});
}
最後若 login 成功,就會回傳 status & token 啦~
附上完整程式碼:
const signToken = id => {
return jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: process.new.JWT_EXPIREIN});
}
exports.login = async(req, res, next) => {
const { email, password } = req.body;
// 針對使用者沒有輸入 email or password 的部分
if (!email || !password){
return next(new AppError('Please provide your email and pawwsord', 400));
}
// 找尋是否有 user,並選取他的 password
const user = await User.findOne({ email }).select('+password');
if(!user || !(await user.passwordIsMatched(password, user.password))){
return next(new AppError('Incorrect email or password'), 401);
}
// 加入 sign token 後
const token = signToken(user._id);
res.status(200).json({
status: 'success',
token,
});
}
今天簡單介紹了 Login api,不得不說實踐安全的程式有很多需要注意的,很感謝 Udemy 課程跟線上文章讓我獲益良多!
Udemy Node.js, Express, MongoDB & More: The Complete Bootcamp 2023
https://www.udemy.com/course/nodejs-express-mongodb-bootcamp/
Hacksplaining:
https://www.hacksplaining.com/