Test and sign in / create account improvements.

This commit is contained in:
2023-04-07 11:04:56 -07:00
parent 6667dd53c1
commit 9428544d99
8 changed files with 91 additions and 59 deletions

View File

@@ -2,6 +2,7 @@ import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
import db from '@/db';
import { withRateLimiter } from '@/lib/rateLimiter';
import { scrypt, randomBytes, timingSafeEqual, randomUUID } from 'crypto';
import { promisify } from 'util';
@@ -44,47 +45,10 @@ const createSubscription = async (userId) => {
await db.run('insert into subscriptions (uuid, user_id) values (?, ?);', randomUUID(), userId);
};
// Rate-limiting settings
const rateLimitWindow = 60 * 1000 * 3; // 3 minute
const maxRequests = 10; // Maximum number of requests within the rateLimitWindow
const rateLimiter = new Map();
const isRateLimited = (ip) => {
const currentTime = Date.now();
const record = rateLimiter.get(ip);
if (record) {
const [requestCount, windowStart] = record;
// If the request is within the rate limit window, update the request count
if (currentTime - windowStart < rateLimitWindow) {
if (requestCount > maxRequests) {
return true;
}
rateLimiter.set(ip, [requestCount + 1, windowStart]);
} else {
// If the request is outside the rate limit window, reset the request count
rateLimiter.set(ip, [1, currentTime]);
}
} else {
// If the IP is not in the rateLimiter, add it
rateLimiter.set(ip, [1, currentTime]);
}
return false;
};
export default async function handler(req, res) {
async function handler(req, res) {
if (req.method === 'POST') {
const { email, password, passwordagain, csi } = req.body;
// Check for rate limiting
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
if (isRateLimited(ip)) {
res.redirect(makeMsg(csi, email, 'Too many requests. Please try again later or send a message on the contact page if you believe this is an error.'));
return;
}
// Validate email, password, and csi
if (email && password && password === passwordagain && csi) {
// Check for minimum password length
@@ -143,3 +107,5 @@ export default async function handler(req, res) {
res.status(405).json({ error: 'Method not allowed. Only POST method is supported.' });
}
}
export default withRateLimiter(handler, true);

View File

@@ -1,10 +1,11 @@
import Stripe from 'stripe';
const stripe = new Stripe('sk_test_51MVz87Ke2JFOuDSNa2PVPrs3BBq9vJQwwDITC3sOB521weM4oklKtQFbJ03MNsJwsxtjHO5NScqOHC9MABREVjU900yYz3lWgL');
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
import { setCookie } from 'cookies-next';
import { v4 as uuidv4 } from 'uuid';
import db from '@/db';
import { withRateLimiter } from '@/lib/rateLimiter';
import { scrypt, randomBytes, timingSafeEqual } from 'crypto';
import { promisify } from 'util';
@@ -14,14 +15,24 @@ const scryptPromise = promisify(scrypt);
async function verify(password, hash, salt, rounds = 64) {
const keyBuffer = Buffer.from(hash, 'hex');
const derivedKey = await scryptPromise(password, salt, rounds);
return timingSafeEqual(keyBuffer, derivedKey);
// Ensure both buffers have the same length
const keyBufferLength = keyBuffer.length;
const derivedKeyLength = derivedKey.length;
const maxLength = Math.max(keyBufferLength, derivedKeyLength);
const paddedKeyBuffer = keyBuffer.length < maxLength ?
Buffer.concat([Buffer.alloc(maxLength - keyBufferLength), keyBuffer]) : keyBuffer;
const paddedDerivedKey = derivedKey.length < maxLength ?
Buffer.concat([Buffer.alloc(maxLength - derivedKeyLength), derivedKey]) : derivedKey;
return timingSafeEqual(paddedKeyBuffer, paddedDerivedKey);
}
function makeMsg(email, text) {
return `/reactors/sign-in?msg=${encodeURIComponent(text)}&email=${encodeURIComponent(email)}`
};
export default async function handler(req, res) {
async function handler(req, res) {
if (req.method === 'POST') {
const { email, password, remember_me: rememberMe } = req.body;
if (email && password) {
@@ -48,6 +59,8 @@ export default async function handler(req, res) {
}
}
} else {
// Handle any other HTTP method
res.status(405).json({ error: 'Method not allowed. Only POST method is supported.' });
}
}
export default withRateLimiter(handler, true);