Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 40 additions & 3 deletions routes/securityQuestion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,42 @@ import { SecurityAnswerModel } from '../models/securityAnswer'
import { UserModel } from '../models/user'
import { SecurityQuestionModel } from '../models/securityQuestion'

const requestCounts: Record<string, { count: number, resetTime: number }> = {}

function verifyCaptcha(req: Request): boolean {
const token = req.headers['x-captcha-token']
return typeof token === 'string' && token.length > 0
}

function rateLimiter(req: Request, res: Response): boolean {
const ip = req.ip
let entry = requestCounts[ip]
if (!entry) {
entry = { count: 0, resetTime: Date.now() + 60 * 60 * 1000 }
}
if (Date.now() > entry.resetTime) {
entry.count = 0
entry.resetTime = Date.now() + 60 * 60 * 1000
}
entry.count++
requestCounts[ip] = entry
if (entry.count > 10) {
res.status(429).json({ error: 'Too many requests' })
return false
}
return true
}

module.exports = function securityQuestion () {
return ({ query }: Request, res: Response, next: NextFunction) => {
const email = query.email
return (req: Request, res: Response, next: NextFunction) => {
if (!verifyCaptcha(req)) {
res.status(400).json({ error: 'Invalid CAPTCHA' })
return
}
if (!rateLimiter(req, res)) {
return
}
const email = req.query.email
SecurityAnswerModel.findOne({
include: [{
model: UserModel,
Expand All @@ -19,11 +52,15 @@ module.exports = function securityQuestion () {
}).then((answer: SecurityAnswerModel | null) => {
if (answer != null) {
SecurityQuestionModel.findByPk(answer.SecurityQuestionId).then((question: SecurityQuestionModel | null) => {
res.json({ question })
// Return actual security question and set resetRequest cookie
res.cookie('resetRequest', answer.SecurityQuestionId.toString(), { httpOnly: true })
res.json({ question: question?.question || '' })
}).catch((error: Error) => {
next(error)
})
} else {
// Dummy cookie to prevent enumeration and maintain consistent flow
res.cookie('resetRequest', 'dummy', { httpOnly: true })
res.json({})
}
}).catch((error: unknown) => {
Expand Down