Real-time threat assessment — failed logins, velocity spikes, impossible travel, credential stuffing.
ThreatDetector
The ThreatDetector analyzes authentication events in real time to produce threat assessments. It maintains sliding windows per user and IP, detecting failed login bursts, velocity spikes, impossible travel, and credential stuffing attacks. Each assessment returns a threat level, a weighted score, and a recommended action.
import { ThreatDetector } from '@codmir/cortex';Constructor
new ThreatDetector(config?: Partial<ThreatDetectorConfig>)ThreatDetectorConfig
interface ThreatDetectorConfig {
maxFailedAttempts: number; // default: 5
failedAttemptWindowMs: number; // default: 900000 (15 minutes)
velocityThreshold: number; // default: 10
velocityWindowMs: number; // default: 60000 (1 minute)
impossibleTravelSpeedKmh: number; // default: 900
minTtlSeconds: number; // default: 300
maxTtlSeconds: number; // default: 900
}| Property | Default | Description |
|---|---|---|
maxFailedAttempts | 5 | Failed login count that triggers the failed_login signal |
failedAttemptWindowMs | 900000 | Sliding window for counting failed logins (15 min) |
velocityThreshold | 10 | Request count per IP that triggers velocity_spike |
velocityWindowMs | 60000 | Sliding window for velocity detection (1 min) |
impossibleTravelSpeedKmh | 900 | Speed threshold for impossible_travel (km/h) |
minTtlSeconds | 300 | Minimum adjusted TTL when threat score is high |
maxTtlSeconds | 900 | Maximum TTL when threat score is zero |
Methods
assess()
Evaluates an authentication event against all detection strategies and returns a threat assessment.
assess(event: AuthEvent): ThreatAssessmentThe event is recorded in the sliding windows before signals are computed.
const assessment = detector.assess({
userId: 'user_abc',
ip: '203.0.113.42',
success: false,
timestamp: Date.now(),
deviceId: 'device_1',
location: { lat: 40.7128, lon: -74.006 },
userAgent: 'Mozilla/5.0',
});
console.log(assessment.level); // 'safe' | 'low' | 'medium' | 'high' | 'critical'
console.log(assessment.action); // 'allow' | 'throttle' | 'reduce_ttl' | 'challenge_mfa' | 'block'
console.log(assessment.requiresMfa); // true if level is 'high' or 'critical'
console.log(assessment.signals); // array of ThreatSignalgetStats()
Returns tracking statistics.
getStats(): {
trackedUsers: number;
trackedIps: number;
trackedLocations: number;
}flush()
Clears all sliding windows and location history.
flush(): voidTypes
AuthEvent
interface AuthEvent {
userId: string;
ip: string;
deviceId?: string;
deviceFingerprint?: string;
success: boolean;
timestamp: number;
location?: { lat: number; lon: number };
userAgent?: string;
}ThreatAssessment
interface ThreatAssessment {
level: ThreatLevel;
score: number;
action: ThreatAction;
signals: ThreatSignal[];
adjustedTtl?: number;
requiresMfa: boolean;
}ThreatLevel
type ThreatLevel = 'safe' | 'low' | 'medium' | 'high' | 'critical';Score thresholds:
| Score | Level |
|---|---|
| 0 - 9 | safe |
| 10 - 29 | low |
| 30 - 59 | medium |
| 60 - 79 | high |
| 80+ | critical |
ThreatAction
type ThreatAction = 'allow' | 'challenge_mfa' | 'reduce_ttl' | 'throttle' | 'block';Each level maps to an action:
| Level | Action |
|---|---|
safe | allow |
low | throttle |
medium | reduce_ttl |
high | challenge_mfa |
critical | block |
ThreatSignal
interface ThreatSignal {
type: 'failed_login' | 'new_device' | 'impossible_travel' | 'velocity_spike' | 'credential_stuffing' | 'token_reuse';
weight: number;
detail: string;
timestamp: number;
}Detection Strategies
Failed Logins
Triggers when the number of failed login attempts for a user exceeds maxFailedAttempts within failedAttemptWindowMs. Weight scales linearly at 15 per failure, capped at 80.
Velocity Spike
Triggers when the number of requests from a single IP exceeds velocityThreshold within velocityWindowMs. Weight scales at 5 per request, capped at 60.
Impossible Travel
Triggers when a user's location changes faster than impossibleTravelSpeedKmh across a distance of at least 100km. Uses the Haversine formula for distance. Fixed weight of 70.
Credential Stuffing
Triggers when 3 or more distinct users fail authentication from the same IP within failedAttemptWindowMs. Weight scales at 20 per unique user, capped at 100.
Example
import { ThreatDetector } from '@codmir/cortex';
const detector = new ThreatDetector({
maxFailedAttempts: 3,
velocityThreshold: 20,
impossibleTravelSpeedKmh: 900,
});
// Simulate 4 failed logins from the same user
for (let i = 0; i < 4; i++) {
const result = detector.assess({
userId: 'user_1',
ip: '10.0.0.1',
success: false,
timestamp: Date.now() + i * 1000,
});
if (result.action === 'block') {
console.log('Blocked:', result.signals.map((s) => s.detail));
break;
}
}
console.log(detector.getStats());
// { trackedUsers: 1, trackedIps: 1, trackedLocations: 0 }
detector.flush();