How Exchanges Track Login Sessions
Technical breakdown of how Binance and Coinbase record every login — IP address, device fingerprint, geolocation, and session lifecycle management.
In this guide
Why Track Logins?
Every reputable exchange records detailed information about every login event. This serves multiple purposes:
- Fraud detection — A login from Nigeria followed by a withdrawal 2 minutes later, when the user only ever logged in from Italy, is a massive red flag.
- Account recovery — When a user reports unauthorized access, the exchange can identify exactly when and from where the attacker logged in.
- Regulatory compliance — KYC/AML regulations require exchanges to maintain access-pattern records. MiCA and the US Bank Secrecy Act both mandate this.
- User transparency — Binance ("Security → Login Activity") and Coinbase ("Activity → Sessions") expose this data so users can self-audit.
This is not unique to crypto — traditional banks and stock brokers maintain similar records. What makes crypto different is the irreversibility of transactions: once a withdrawal is confirmed on-chain, it cannot be reversed.
The Login Sessions Table
Here is the schema used by production-grade exchanges:
CREATE TABLE login_sessions (
id UUID PRIMARY KEY,
user_id UUID NOT NULL REFERENCES users(id),
ip_address INET NOT NULL,
country VARCHAR(4),
city VARCHAR(100),
region VARCHAR(100),
user_agent TEXT,
device_type VARCHAR(20),
os VARCHAR(50),
browser VARCHAR(50),
status VARCHAR(16) DEFAULT 'active',
is_current BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
last_active_at TIMESTAMPTZ DEFAULT NOW(),
expires_at TIMESTAMPTZ,
revoked_at TIMESTAMPTZ
);
What each field captures:
- ip_address — Public IP extracted from X-Forwarded-For or X-Real-IP headers behind a load balancer.
- country/city/region — Resolved server-side using MaxMind GeoIP2 database (IP-to-location lookup, not GPS).
- user_agent — Raw HTTP header, parsed into device_type, os, and browser using libraries like ua-parser-js.
- status — Lifecycle tracking: 'active', 'expired', 'revoked' (manually logged out or admin-terminated).
IP Geolocation
When a user logs in, the server resolves their IP to a geographic location:
1. IP extraction:
``typescript
const ip = req.headers.get('x-forwarded-for')?.split(',')[0]?.trim()
|| req.headers.get('x-real-ip')
|| '127.0.0.1';
``
2. Geolocation lookup (MaxMind GeoIP2, ~70MB offline DB):
``typescript
import maxmind from 'maxmind';
const geo = await maxmind.open('GeoLite2-City.mmdb');
const result = geo.get(ip);
// result.country.iso_code → 'IT'
// result.city.names.en → 'Milan'
``
Accuracy: City-level ~60-80%, country-level ~99%. VPN/Tor users show the exit node's location.
Binance displays login history with: Date, IP Address, Device, Location, Status. They also send email alerts for logins from new IPs or new countries.
Coinbase shows "Active Sessions" with device name, location, last active time, and per-session "Sign Out" button. They use device trust scoring — a known device on a known network gets less friction.
Suspicious Login Detection
Exchanges run automated detection rules on every login:
Rule-based detection: - New IP — First time this IP seen for this user → flag for review. - New country — Login from a new country → email alert + potential 2FA re-verification. - Impossible travel — Login from Italy, then Japan 30 minutes later → account lock. - Tor/VPN exit node — IP matches known Tor relays or VPN endpoints → heightened scrutiny. - Rate limiting — More than 5 failed attempts in 10 minutes → temporary IP ban + account lockout.
Implementation (PostgreSQL view):
``sql
CREATE VIEW v_suspicious_logins AS
SELECT ls.*,
CASE WHEN ls.ip_address NOT IN (
SELECT DISTINCT ip_address FROM login_sessions prev
WHERE prev.user_id = ls.user_id AND prev.id != ls.id
) THEN TRUE ELSE FALSE END AS is_new_ip,
CASE WHEN ls.country NOT IN (
SELECT DISTINCT country FROM login_sessions prev
WHERE prev.user_id = ls.user_id AND prev.id != ls.id
) THEN TRUE ELSE FALSE END AS is_new_country
FROM login_sessions ls
WHERE ls.created_at > NOW() - INTERVAL '30 days';
``
Binance implements a 24-hour withdrawal lock when a new device or IP is detected. They require re-entering 2FA for any withdrawal from an unrecognized session.
Session Lifecycle Management
Sessions are not permanent. Exchanges implement strict lifecycle rules:
Expiration: - Web sessions: 7 days (Binance), 30 days (Coinbase) of inactivity. - API sessions: Based on API key expiry, no implicit timeout. - Mobile: Longer-lived sessions with biometric re-authentication.
Revocation: Users can manually revoke sessions from security settings. When revoked, the JWT is added to a blocklist (Redis, O(1) lookup on every request).
Force logout all: Triggered by critical events: - Password change - 2FA enable/disable - Account compromise detected
Token invalidation: JWT tokens are stateless, so exchanges use a Redis blocklist of revoked token IDs (JTI claim). Every API route checks this blocklist. The TTL on each entry matches the JWT's remaining lifetime.
// On password change, revoke all sessions except current
await pool.query(
'UPDATE login_sessions SET status = $1, revoked_at = NOW() WHERE user_id = $2 AND id != $3 AND status = $4',
['revoked', userId, currentSessionId, 'active']
);
Practice in a risk-free environment
Apply the concepts using virtual funds and live market data. NexChange is an educational simulation, not a real-money exchange.
Continue learning
Related guides
How Crypto Exchanges Store Every Operation
A deep technical dive into the database architecture of real crypto exchanges — how Binance and Coinbase track every deposit, trade, withdrawal, and audit event per user.
Securing Your Crypto Assets
Essential security practices to protect your cryptocurrency from hackers, scams, and human error.
The Real User Flow: Registration to Withdrawal
A developer-level walkthrough of what happens at every step when a user interacts with a centralized exchange — from database writes to blockchain transactions.