Skip to content

Commit 9858410

Browse files
Archithclaude
andcommitted
feat: Implement comprehensive IP tracking and session security
- Add IP tracking API endpoints using Vercel serverless functions - Implement offline geolocation using geoip-lite (no external APIs) - Add VPN/proxy detection based on datacenter IPs and headers - Implement multiple login detection from different IPs - Create session tracking module for frontend integration - Add security checks when users join sessions - Track device fingerprinting and browser information - Add heartbeat system to maintain active sessions Security features: - Hash IPs for privacy protection - Detect suspicious proxy chains - Monitor timezone mismatches - Track all security flags per session 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e6f6b29 commit 9858410

6 files changed

Lines changed: 646 additions & 15 deletions

File tree

api/check-duplicate-login.js

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Check for duplicate/multiple logins from different IPs
2+
import crypto from 'crypto';
3+
4+
// Simple in-memory store for active sessions (in production, use Redis or Firebase)
5+
const activeSessions = new Map();
6+
7+
// Clean up old sessions every 5 minutes
8+
setInterval(() => {
9+
const now = Date.now();
10+
for (const [key, session] of activeSessions.entries()) {
11+
if (now - session.lastActivity > 30 * 60 * 1000) { // 30 minutes
12+
activeSessions.delete(key);
13+
}
14+
}
15+
}, 5 * 60 * 1000);
16+
17+
function hashIP(ip) {
18+
return crypto.createHash('sha256').update(ip + process.env.JWT_SECRET).digest('hex').substring(0, 16);
19+
}
20+
21+
export default async function handler(req, res) {
22+
// Enable CORS
23+
res.setHeader('Access-Control-Allow-Credentials', true);
24+
res.setHeader('Access-Control-Allow-Origin', '*');
25+
res.setHeader('Access-Control-Allow-Methods', 'POST,OPTIONS');
26+
27+
if (req.method === 'OPTIONS') {
28+
res.status(200).end();
29+
return;
30+
}
31+
32+
if (req.method !== 'POST') {
33+
return res.status(405).json({ error: 'Method not allowed' });
34+
}
35+
36+
try {
37+
const { userId, userName, sessionCode, action } = req.body;
38+
39+
if (!userId || !sessionCode) {
40+
return res.status(400).json({ error: 'Missing required fields' });
41+
}
42+
43+
// Get IP
44+
const ip = req.headers['x-forwarded-for']?.split(',')[0].trim() ||
45+
req.headers['x-real-ip'] ||
46+
req.connection?.remoteAddress ||
47+
'0.0.0.0';
48+
49+
const hashedIP = hashIP(ip);
50+
const sessionKey = `${sessionCode}-${userId}`;
51+
52+
// Handle different actions
53+
if (action === 'login') {
54+
// Check if user is already logged in from different IP
55+
const existingSession = activeSessions.get(sessionKey);
56+
57+
if (existingSession && existingSession.ip !== hashedIP) {
58+
// User is already logged in from different IP
59+
const timeSinceLastActivity = Date.now() - existingSession.lastActivity;
60+
61+
if (timeSinceLastActivity < 5 * 60 * 1000) { // Active in last 5 minutes
62+
return res.status(403).json({
63+
error: 'Multiple login detected',
64+
message: 'You are already logged into this session from another location',
65+
existingLocation: existingSession.location,
66+
existingDevice: existingSession.device,
67+
lastActivity: new Date(existingSession.lastActivity).toISOString()
68+
});
69+
}
70+
}
71+
72+
// Store new session
73+
activeSessions.set(sessionKey, {
74+
userId,
75+
userName,
76+
sessionCode,
77+
ip: hashedIP,
78+
location: req.headers['cf-ipcountry'] || 'Unknown', // Cloudflare geo header
79+
device: req.headers['user-agent']?.substring(0, 50),
80+
loginTime: Date.now(),
81+
lastActivity: Date.now()
82+
});
83+
84+
return res.status(200).json({
85+
success: true,
86+
message: 'Login tracked successfully'
87+
});
88+
89+
} else if (action === 'heartbeat') {
90+
// Update last activity
91+
const session = activeSessions.get(sessionKey);
92+
if (session) {
93+
session.lastActivity = Date.now();
94+
}
95+
96+
return res.status(200).json({ success: true });
97+
98+
} else if (action === 'logout') {
99+
// Remove session
100+
activeSessions.delete(sessionKey);
101+
102+
return res.status(200).json({
103+
success: true,
104+
message: 'Logout successful'
105+
});
106+
107+
} else if (action === 'check') {
108+
// Check if session exists
109+
const session = activeSessions.get(sessionKey);
110+
111+
return res.status(200).json({
112+
exists: !!session,
113+
session: session ? {
114+
loginTime: session.loginTime,
115+
lastActivity: session.lastActivity,
116+
location: session.location
117+
} : null
118+
});
119+
}
120+
121+
return res.status(400).json({ error: 'Invalid action' });
122+
123+
} catch (error) {
124+
console.error('Duplicate login check error:', error);
125+
res.status(500).json({ error: 'Failed to check login status' });
126+
}
127+
}

0 commit comments

Comments
 (0)