# Login location tracking (country/city/IP)

- **Pattern:** `ab-001471` (`finserv-session-security.concurrent-control.login-location-tracking`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001471
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001471)

## Why it matters

Login location tracking is the baseline dataset for detecting impossible travel, account sharing, and unauthorized access patterns. Without it, there is no ground truth for anomaly detection — you cannot identify a login from Russia 10 minutes after a login from New York if no location is recorded. PCI-DSS Req 10.2.1 requires that access attempts include origin context. NIST AU-3 mandates that audit records capture location where applicable. ISO 27001:2022 A.8.15 requires traceability of access events. In financial fraud investigations, login location records are often the primary evidence used to distinguish legitimate from unauthorized transactions.

## Severity rationale

Low because location tracking does not prevent unauthorized access on its own, but without it, anomaly detection, fraud investigation, and incident response lose the geographic context needed to identify suspicious login patterns.

## Remediation

Add geolocation capture to the login handler in `src/lib/geoLocation.ts` using a local lookup library to avoid external API dependency:

```js
import geoip from 'geoip-lite'; // local MaxMind DB — no external calls

export function trackLoginLocation(req: Request) {
  const ip = getClientIP(req);
  const geo = geoip.lookup(ip);
  return {
    ip,
    country: geo?.country ?? null,
    city: geo?.city ?? null,
    timestamp: new Date()
  };
}
```

Store the result on the session record at creation time:

```js
const location = trackLoginLocation(req);
await db.sessions.create({
  data: { userId, location, device: parseUserAgent(req), expiresAt }
});
```

Capture at minimum: IP, country, and city — that is the three-field minimum. Keep `geoip-lite`'s MaxMind database updated monthly to maintain lookup accuracy.

## Detection

- **ID:** `login-location-tracking`
- **Severity:** `low`
- **What to look for:** Count all location tracking data fields in session records (IP, country, city, region). Quote the actual geolocation library found (geoip-lite, maxmind, ip2location). Enumerate the session columns that store location data. Verify at least 3 location fields are recorded per login.
- **Pass criteria:** Login attempts record at least 3 location fields: IP address, country, and city. Data is stored with the session record. Report the count even on pass (e.g., "3 location fields: ip, country, city — via geoip-lite, stored in sessions table").
- **Fail criteria:** Login location not tracked (0 location fields), or only IP recorded without geolocation (fewer than 3 fields).
- **Skip (N/A) when:** The application does not require location tracking — cite the actual compliance requirements found.
- **Detail on fail:** `"0 location fields recorded on login — no IP, no geolocation"` or `"IP recorded but 0 geolocation fields (no country or city) — 1 of 3 required fields"`
- **Remediation:** Add IP geolocation to login (in `src/lib/geoLocation.ts`):
  ```js
  // lib/geoLocation.ts
  import geoip from 'geoip-lite';

  export async function trackLoginLocation(req: Request) {
    const ip = getClientIP(req);
    const geo = geoip.lookup(ip);
    return {
      ip,
      country: geo?.country,
      city: geo?.city,
      timestamp: new Date()
    };
  }

  // In login endpoint
  const location = await trackLoginLocation(req);
  await db.sessions.create({
    userId,
    location,
    device: parseUserAgent(req),
    expiresAt: sessionExpiry
  });
  ```

---

## External references

- nist AU-3
- pci-dss Req 10.2.1
- iso-27001 A.8.15

Taxons: observability

HTML version: https://auditbuffet.com/patterns/ab-001471
