7.5 KiB
7.5 KiB
Phone Number & Device Management - Security Scenarios
Current System Behavior
Scenario 1: Two Different Users with Same Phone Number
Current Behavior:
- ❌ SECURITY ISSUE: Both users will access the SAME account
- The system uses "find or create" logic:
- If phone number exists → Logs into existing account
- If phone number doesn't exist → Creates new account
- The second person can:
- See the first person's data
- Modify the first person's profile
- Access their listings/transactions
- Change their account settings
Why this happens:
// src/routes/authRoutes.js (lines 88-107)
// find or create user
const found = await db.query(`SELECT ... FROM users WHERE phone_number = $1`, [phone]);
if (found.rows.length === 0) {
// Create new user
} else {
user = found.rows[0]; // ← Uses existing account!
}
Database Constraint:
phone_numberhas UNIQUE constraint (one account per phone number)
Scenario 2: Same User, Different Device
Current Behavior:
- ✅ Works correctly: Same user can log in from multiple devices
- Each device gets:
- Its own refresh token (tracked by
device_id) - Separate device record in
user_devicestable - Independent session
- Its own refresh token (tracked by
- All devices can be logged in simultaneously
- Each device's tokens work independently
How it works:
// Device tracking (lines 117-142)
INSERT INTO user_devices (user_id, device_identifier, ...)
VALUES ($1, $2, ...)
ON CONFLICT (user_id, device_identifier)
DO UPDATE SET last_seen_at = NOW(), ...
Database Constraint:
UNIQUE (user_id, device_identifier)→ Same device can't be registered twice for same user
Example:
- User logs in on Phone A → Gets tokens, device_id = "phone-a"
- User logs in on Phone B → Gets different tokens, device_id = "phone-b"
- Both devices active simultaneously
- Logout on Phone A → Only revokes tokens for device_id = "phone-a"
- Phone B continues working
Security Risks
Risk 1: Phone Number Hijacking
Problem:
- If someone gets access to your phone number (SIM swap, lost phone), they can log into your account
- They receive the OTP and gain full account access
Mitigation (Recommended):
- Add additional verification (email, recovery questions)
- Implement device fingerprinting
- Alert user on new device login
- Allow user to see all active devices and revoke them
Risk 2: Shared Phone Numbers
Problem:
- Family members sharing a phone
- Business phone used by multiple employees
- Second-hand phone numbers
Current Impact:
- Account confusion
- Data privacy violations
- Unauthorized access
Recommended Solutions
Solution 1: Warn User on First Login from New Device
Implementation:
// In verify-otp endpoint
const existingDevices = await db.query(
`SELECT COUNT(*) FROM user_devices WHERE user_id = $1`,
[user.id]
);
if (existingDevices.rows[0].count > 0) {
// This is a new device for existing account
// Could send notification to all other devices
// Or require additional verification
}
Solution 2: Multi-Factor Authentication (MFA)
Add options:
- Email verification for new device
- SMS backup codes
- Recovery questions
- Authenticator app
Solution 3: Account Ownership Verification
Before creating account:
// Check if phone number recently used
const recentLogins = await db.query(
`SELECT user_id, last_login_at
FROM users
WHERE phone_number = $1
AND last_login_at > NOW() - INTERVAL '7 days'`,
[phoneNumber]
);
if (recentLogins.rows.length > 0) {
// Require additional verification or block
}
Solution 4: Device Management Endpoint
Add API endpoints:
// GET /users/me/devices - List all active devices
// DELETE /users/me/devices/:device_id - Revoke specific device
// POST /users/me/devices/:device_id/verify - Verify device ownership
Solution 5: Session Limits
Limit concurrent sessions:
// Enforce maximum devices per user
const MAX_DEVICES = 5;
const deviceCount = await db.query(
`SELECT COUNT(*) FROM user_devices WHERE user_id = $1 AND is_active = true`,
[user.id]
);
if (deviceCount >= MAX_DEVICES) {
// Revoke oldest device or require user to choose which to revoke
}
Immediate Actions Needed
1. Document Current Behavior
- Update API documentation
- Warn developers about phone number uniqueness
- Add to user terms of service
2. Add Logging
// Log all login attempts
await db.query(
`INSERT INTO auth_audit (user_id, action, status, device_id, ip_address)
VALUES ($1, 'login', 'success', $2, $3)`,
[user.id, devId, req.ip]
);
3. Add User Notifications
- Email/SMS alert when login from new device
- Show active devices in user profile
- Allow device revocation
4. Consider Account Recovery Flow
- Allow users to dispute account ownership
- Support team can transfer ownership
- Require additional verification for sensitive actions
Testing Scenarios
Test Case 1: Same Phone, Different Users
1. User A requests OTP for +919876543210
2. User A verifies OTP → Account created
3. User B requests OTP for +919876543210
4. User B verifies OTP → Logs into User A's account ❌
Test Case 2: Same User, Multiple Devices
1. User logs in on Device A → Gets tokens
2. User logs in on Device B → Gets different tokens
3. Both tokens work simultaneously ✅
4. User logs out on Device A → Device B still works ✅
Test Case 3: Device Re-login
1. User logs in on Device A
2. User logs out
3. User logs in again on Device A → New tokens issued ✅
Best Practices for Mobile App
1. Inform Users
// Show warning in app
if (response.needs_profile && existingAccount) {
showDialog("This phone number is already registered.
You will be logged into the existing account.")
}
2. Show Active Devices
// Display all logged-in devices
GET /users/me/devices → List devices with last_seen_at
3. Allow Device Management
// Let users revoke devices
DELETE /users/me/devices/{device_id}
4. Handle Token Revocation
// If refresh returns 401, check if device was revoked
if (error.code == 401) {
checkActiveDevices()
if (currentDeviceRevoked) {
forceReLogin()
}
}
Database Queries for Analysis
Check accounts by phone
SELECT phone_number, COUNT(*) as account_count
FROM users
GROUP BY phone_number
HAVING COUNT(*) > 1;
-- Should return 0 rows (UNIQUE constraint)
Check devices per user
SELECT u.phone_number, COUNT(ud.id) as device_count
FROM users u
LEFT JOIN user_devices ud ON u.id = ud.user_id
WHERE ud.is_active = true
GROUP BY u.id, u.phone_number
ORDER BY device_count DESC;
Check concurrent sessions
SELECT u.phone_number, ud.device_identifier, ud.last_seen_at
FROM users u
JOIN user_devices ud ON u.id = ud.user_id
WHERE ud.is_active = true
AND ud.last_seen_at > NOW() - INTERVAL '1 hour'
ORDER BY ud.last_seen_at DESC;
Summary
| Scenario | Current Behavior | Risk Level | Action Needed |
|---|---|---|---|
| Same phone, different users | Share same account | 🔴 HIGH | Add verification/alert |
| Same user, different devices | Multiple active sessions | 🟢 LOW | Add device management UI |
| Same device, multiple logins | Works (token refresh) | 🟢 LOW | None |
| Phone number hijacking | Full account access | 🔴 HIGH | Add MFA, alerts |