iT邦幫忙

2023 iThome 鐵人賽

DAY 27
0
Security

從自建漏洞中學習 - 一起填坑吧系列 第 27

Auth 應用程式 - Reset passord 篇之 3

  • 分享至 

  • xImage
  •  

Auth 應用程式 - Reset passord 篇之 3

設置 resetPassword function 的過程

在先前我們建置好寄送 email 的服務後,我們接著來建立 resetPassword 的 function:

在 resetPassword function 中,我們會需要做以下幾件事:

  1. 透過 token 取得 user,讓 server 透過比對先前帶的 token 來找出 user
  2. 若 token 沒有過期,開始設置 password,並且將 resetPassword 相關的參數重新設置成 undefined
  3. 更新 changedPasswordAt 參數並儲存,讓之後 user 在 access 時可以有資料和 JWT 的 iat 的時間做比較
  4. 讓 user 登入 - send JWT

實作 resetPassword function

在 AuthController 中,我們建立一個 resetPassword function:

resetPassword function:

exports.resetPassword = catchAsync(
	async (req, res, next) => {
		// 1. 透過加密的 token ,尋找儲存在 DB 中的 user
		const hashToken = crypto
			.createHash('sha256')
			.update(req.params.token)
			.digest('hex');
			
		const user = await User.findOne({
			    tokenForResetPassword: hashToken,
			    passwordResetExpiresIn: { $gt: Date.now() }
		});
		// 上述的 $gt 是 greater than 的意思,詳細可以參考最後的小百科
		
		// 2. 若 token 沒有過期 -> 開始設置 password,並且將 resetPassword 相關的參數重新設置成 undefined
        
		// 2-1. 找不到 user 的情況: 代表 token 失效或是過期,回傳 400 錯誤
		if (!user) {
			return next(new AppError('Token has expired or is invalid.', 400));
		}
        
		// 2-2. 找到 user 的情況下,重設 password、
		user.password = req.body.password;
		user.passwordConfirmed = req.body.passwordConfirmed;
		user.tokenForResetPassword = undefined;
		user.passwordResetExpiresIn = undefined;
		// 此處 save,並且不需要關閉 validate,因為我們需要驗證
		await user.save();
		
		// 3. 更新 changedPasswordAt (此處的程式碼我們會標示在下一個程式碼區塊中)
        
		// 4. 登入 user 並且 send JWT
        const signedToken = signToken(user._id);
        
        res.status(200).json({
        	status: 'success',
        	token: signedToken
        })
	}
)

在 userModel.js 中,我們會建立一個 mongoose pre save middleware:

userSchema.pre('save', async function(next) => {
	// 當 password 沒有被更新或是 Document 是 New 的時候,不用更新passwordChangedAt
	if (!this.isModified('password') || this.isNew) return next();
	
	// 設置 passwordChangedAt
	// 不使用 this.passwordChangedAt = new Date.now(); 的原因: 因為存到 DB 的 passwordChangedAt timestamp 有可能會比發出 JWT 上面所記錄的 iat timestamp 還要大 (也就是時間有可能比較晚),所以需要做一個時間上的微幅調整
	this.passwordChangedAt = Date.now() - 1000;
	next();
	
});

小百科:

還記得之前的 $gt 嗎 ?

MongoDB 中,$gt 的定義是:
選擇指定欄位的值大於(即 >)指定值的文件。

form 大致如下:

{ field: { $gt: value } }

(可以參考 MongoDB 官方網站: https://www.mongodb.com/docs/manual/reference/operator/query/gt/)


今日小心得

今天大致上介紹到這邊~

不知不覺參賽來到了第 27 天,感覺上還有一大堆內容沒寫完...

希望之後能順利完賽


Reference


上一篇
Auth 應用程式 - Reset passord 篇之 2
下一篇
Auth 應用程式 - Authorization 授權篇
系列文
從自建漏洞中學習 - 一起填坑吧30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言