UNPKG

@cocalc/server

Version:

CoCalc server functionality: functions used by either the hub and the next.js server

70 lines (63 loc) 2.7 kB
/* Some simple sign in throttling to reduce the impact of brute force attacks. This is in memory per-backend server, and doesn't touch the database. */ import getStrategies from "@cocalc/server/auth/sso/get-strategies"; import LRU from "lru-cache"; import { checkRequiredSSO } from "./sso/check-required-sso"; const emailShortCache = new LRU<string, number>({ max: 10000, // avoid memory issues maxAge: 1000 * 60, }); const emailLongCache = new LRU<string, number>({ max: 20000, maxAge: 1000 * 60 * 60, }); const ipShortCache = new LRU<string, number>({ max: 10000, maxAge: 1000 * 60 }); const ipLongCache = new LRU<string, number>({ max: 20000, maxAge: 1000 * 60 * 60, }); async function isExclusiveEmail(email: string) { const strategies = await getStrategies(); return checkRequiredSSO({ email, strategies }); } export async function signInCheck( email: string, ip?: string, auth_token: boolean = false ): Promise<string | undefined> { if ((emailShortCache.get(email) ?? 0) > 5) { // A given email address is allowed at most 5 failed login attempts per minute return `Too many attempts per minute to sign in as "${email}". Wait one minute, then try again.`; } if ((emailLongCache.get(email) ?? 0) > 50) { // A given email address is allowed at most 50 failed login attempts per hour. return `Too many attempts per hour to sign in as "${email}". Wait about an hour, then try again.`; } if (ip != null && (ipShortCache.get(ip) ?? 0) > 30) { // A given ip address is allowed at most 30 failed login attempts per minute. return `Too many attempts per minute to sign in from your computer. Wait one minute, then try again.`; } if (ip != null && (ipLongCache.get(ip) ?? 0) > 200) { // A given ip address is allowed at most 200 failed login attempts per hour. return `Too many attempts per hour to sign in from your computer. Wait about an hour, then try again.`; } // unless user has an auth token, we check if the email address is part of an exclusive SSO mechanism (and block password sign ins) if (!auth_token) { const exclusiveSSO = await isExclusiveEmail(email); if (exclusiveSSO != null) { const name = exclusiveSSO.display ?? exclusiveSSO.name; return `You have to sign in using the Single-Sign-On mechanism "${name}" of your institution.`; } } } export function recordFail(email: string, ip?: string): void { emailShortCache.set(email, (emailShortCache.get(email) ?? 0) + 1); emailLongCache.set(email, (emailLongCache.get(email) ?? 0) + 1); if (ip != null) { ipShortCache.set(ip, (ipShortCache.get(ip) ?? 0) + 1); ipLongCache.set(ip, (ipLongCache.get(ip) ?? 0) + 1); } }