mirror of
https://github.com/DumbWareio/DumbDrop.git
synced 2025-10-23 07:41:58 +00:00
feat: pin not working when rate limited redirect fix & allow non https baseUrl pin fix (#32)
* feat: ratelimit pin not working with baseUrl fix * Remove white space changes * Refactor PIN verification error handling and input state management - Improve error handling in login page JavaScript - Standardize API response structure with explicit success and error fields - Enhance user feedback for PIN authentication failures - Implement more robust input state management during login attempts * Fix PIN verification logic in root route - Improve PIN verification check to handle missing cookie scenario - Add explicit check for cookie existence before comparing PIN - Enhance root route authentication logic for more robust access control
This commit is contained in:
@@ -78,7 +78,6 @@
|
|||||||
const createPinInputs = (length) => {
|
const createPinInputs = (length) => {
|
||||||
const form = document.getElementById('pin-form');
|
const form = document.getElementById('pin-form');
|
||||||
form.innerHTML = ''; // Clear existing inputs
|
form.innerHTML = ''; // Clear existing inputs
|
||||||
|
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
input.type = 'password';
|
input.type = 'password';
|
||||||
@@ -130,29 +129,32 @@
|
|||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ pin })
|
body: JSON.stringify({ pin })
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
|
// Simplified success and error handling
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
} else {
|
} else {
|
||||||
// Show error message
|
// Show error message
|
||||||
document.getElementById('pin-error').textContent = data.error;
|
document.getElementById('pin-error').textContent = data.error || 'Authentication failed';
|
||||||
|
|
||||||
|
// Determine if it's a lockout scenario
|
||||||
|
const isLockedOut = data.error.includes('Too many PIN verification attempts');
|
||||||
|
|
||||||
// Only clear inputs and focus if not locked out
|
|
||||||
if (!data.error.includes('try again in')) {
|
|
||||||
const inputs = [...document.querySelectorAll('.pin-digit')];
|
const inputs = [...document.querySelectorAll('.pin-digit')];
|
||||||
|
if (isLockedOut) {
|
||||||
|
// Disable inputs if locked out
|
||||||
|
inputs.forEach(input => {
|
||||||
|
input.disabled = true;
|
||||||
|
input.classList.add('locked');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Reset inputs for other error types
|
||||||
inputs.forEach(input => {
|
inputs.forEach(input => {
|
||||||
input.value = '';
|
input.value = '';
|
||||||
input.classList.remove('filled');
|
input.classList.remove('filled');
|
||||||
});
|
});
|
||||||
inputs[0].focus();
|
inputs[0].focus();
|
||||||
} else {
|
|
||||||
// If locked out, disable all inputs
|
|
||||||
const inputs = [...document.querySelectorAll('.pin-digit')];
|
|
||||||
inputs.forEach(input => {
|
|
||||||
input.disabled = true;
|
|
||||||
input.classList.add('locked');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@@ -14,6 +14,7 @@ const { config, validateConfig } = require('./config');
|
|||||||
const logger = require('./utils/logger');
|
const logger = require('./utils/logger');
|
||||||
const { ensureDirectoryExists } = require('./utils/fileUtils');
|
const { ensureDirectoryExists } = require('./utils/fileUtils');
|
||||||
const { securityHeaders, requirePin } = require('./middleware/security');
|
const { securityHeaders, requirePin } = require('./middleware/security');
|
||||||
|
const { safeCompare } = require('./utils/security');
|
||||||
const { initUploadLimiter, pinVerifyLimiter, downloadLimiter } = require('./middleware/rateLimiter');
|
const { initUploadLimiter, pinVerifyLimiter, downloadLimiter } = require('./middleware/rateLimiter');
|
||||||
|
|
||||||
// Create Express app
|
// Create Express app
|
||||||
@@ -40,7 +41,8 @@ app.use('/api/files', requirePin(config.pin), downloadLimiter, fileRoutes);
|
|||||||
|
|
||||||
// Root route
|
// Root route
|
||||||
app.get('/', (req, res) => {
|
app.get('/', (req, res) => {
|
||||||
if (config.pin && !req.cookies.DUMBDROP_PIN) {
|
// Check if the PIN is configured and the cookie exists
|
||||||
|
if (config.pin && (!req.cookies?.DUMBDROP_PIN || !safeCompare(req.cookies.DUMBDROP_PIN, config.pin))) {
|
||||||
return res.redirect('/login.html');
|
return res.redirect('/login.html');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,11 +24,11 @@ router.post('/verify-pin', (req, res) => {
|
|||||||
if (!config.pin) {
|
if (!config.pin) {
|
||||||
res.cookie('DUMBDROP_PIN', '', {
|
res.cookie('DUMBDROP_PIN', '', {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: process.env.NODE_ENV === 'production',
|
secure: req.secure || (process.env.NODE_ENV === 'production' && config.baseUrl.startsWith('https')),
|
||||||
sameSite: 'strict',
|
sameSite: 'strict',
|
||||||
path: '/'
|
path: '/'
|
||||||
});
|
});
|
||||||
return res.json({ success: true });
|
return res.json({ success: true, error: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate PIN format
|
// Validate PIN format
|
||||||
@@ -36,6 +36,7 @@ router.post('/verify-pin', (req, res) => {
|
|||||||
if (!cleanedPin) {
|
if (!cleanedPin) {
|
||||||
logger.warn(`Invalid PIN format from IP: ${ip}`);
|
logger.warn(`Invalid PIN format from IP: ${ip}`);
|
||||||
return res.status(401).json({
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
error: 'Invalid PIN format. PIN must be 4-10 digits.'
|
error: 'Invalid PIN format. PIN must be 4-10 digits.'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -49,7 +50,8 @@ router.post('/verify-pin', (req, res) => {
|
|||||||
|
|
||||||
logger.warn(`Login attempt from locked out IP: ${ip}`);
|
logger.warn(`Login attempt from locked out IP: ${ip}`);
|
||||||
return res.status(429).json({
|
return res.status(429).json({
|
||||||
error: `Too many attempts. Please try again in ${timeLeft} minutes.`
|
success: false,
|
||||||
|
error: `Too many PIN verification attempts. Please try again in ${timeLeft} minutes.`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,13 +63,13 @@ router.post('/verify-pin', (req, res) => {
|
|||||||
// Set secure cookie with cleaned PIN
|
// Set secure cookie with cleaned PIN
|
||||||
res.cookie('DUMBDROP_PIN', cleanedPin, {
|
res.cookie('DUMBDROP_PIN', cleanedPin, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: process.env.NODE_ENV === 'production',
|
secure: req.secure || (process.env.NODE_ENV === 'production' && config.baseUrl.startsWith('https')),
|
||||||
sameSite: 'strict',
|
sameSite: 'strict',
|
||||||
path: '/'
|
path: '/'
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info(`Successful PIN verification from IP: ${ip}`);
|
logger.info(`Successful PIN verification from IP: ${ip}`);
|
||||||
res.json({ success: true });
|
res.json({ success: true, error: null });
|
||||||
} else {
|
} else {
|
||||||
// Record failed attempt
|
// Record failed attempt
|
||||||
const attempts = recordAttempt(ip);
|
const attempts = recordAttempt(ip);
|
||||||
@@ -78,12 +80,12 @@ router.post('/verify-pin', (req, res) => {
|
|||||||
success: false,
|
success: false,
|
||||||
error: attemptsLeft > 0 ?
|
error: attemptsLeft > 0 ?
|
||||||
`Invalid PIN. ${attemptsLeft} attempts remaining.` :
|
`Invalid PIN. ${attemptsLeft} attempts remaining.` :
|
||||||
'Too many attempts. Account locked for 15 minutes.'
|
'Too many PIN verification attempts. Account locked for 15 minutes.'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(`PIN verification error: ${err.message}`);
|
logger.error(`PIN verification error: ${err.message}`);
|
||||||
res.status(500).json({ error: 'Authentication failed' });
|
res.status(500).json({ success: false, error: 'Authentication failed' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user