# No tracking identifiers read before ATT consent is received

- **Pattern:** `ab-000474` (`app-store-privacy-data.tracking-advertising.no-tracking-before-consent`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000474
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000474)

## Why it matters

Displaying an ATT prompt but reading the tracking identifier before the user responds — or transmitting a device-persistent ID to a third party before ATT resolves — defeats the purpose of the consent mechanism entirely. Under GDPR Art.7, consent must precede processing; under CCPA §1798.120, users have the right to opt out before sharing occurs. OWASP 2021 A04 (Insecure Design) applies when consent architecture is structurally bypassed. Apple's App Review detects this pattern with instrumentation: they see SDK network calls before the ATT dialog appears in their testing, and apps fail review for it.

## Severity rationale

High because transmitting tracking identifiers before ATT consent is obtained constitutes pre-consent data collection that violates Apple policy, GDPR Art.7, and CCPA §1798.120.

## Remediation

Gate every identifier read and third-party transmission on ATT status resolution:

```ts
import { getTrackingStatus } from 'expo-tracking-transparency';
const status = await getTrackingStatus();
if (status === 'authorized') {
  const advertisingId = await getAdvertisingId();
  analytics.identify(advertisingId);
} else {
  // Use ephemeral session ID only
}
```

Audit every `track()` and `identify()` call in `src/analytics/` or equivalent for whether it includes a device-persistent or user-persistent identifier. Replace persistent device IDs with ephemeral session IDs (generated fresh each app launch) for pre-consent analytics. Any `DeviceInfo.getUniqueId()` value passed to a third-party service must be gated the same way.

## Detection

- **ID:** `no-tracking-before-consent`
- **Severity:** `high`
- **What to look for:** Count all relevant instances and enumerate each. This check goes deeper than the ATT framework check — look for any code path that reads tracking-adjacent identifiers before consent: `UIDevice.current.identifierForVendor` (IDFV — not IDFA, but if shared across apps it may require ATT); any hashed email or phone number sent to an ad network before login; fingerprinting signals (screen resolution, OS version, installed font list, battery level, CPU count) collected and sent to a third-party server; `DeviceInfo.getUniqueId()` (from `react-native-device-info`) sent to an analytics service before ATT consent. For React Native, search for `NativeModules.RNDeviceInfo`, `DeviceInfo.getUniqueId`, `DeviceInfo.getAdvertisingId`, `getAdvertisingId()`, `TrackingTransparency.getAdvertisingId()` calls without a preceding ATT status check. For Flutter, look for `AdvertisingId.id` or `device_info_plus` unique ID usage passed to analytics before consent. Also check whether any analytics event sent on app launch includes a persistent user or device identifier.
- **Pass criteria:** No tracking identifier is read or transmitted to a third party before ATT consent is obtained. At least 1 implementation must be verified. IDFV is only used for first-party analytics (not shared cross-app or with ad networks before consent).
- **Fail criteria:** Any tracking identifier read or transmitted to a third party before the ATT prompt completes and receives user authorization.
- **Skip (N/A) when:** No advertising or cross-app tracking SDKs detected; app does not read IDFA or share device identifiers with third parties.
- **Detail on fail:** `"getAdvertisingId() called in src/analytics/startup.ts before ATT prompt — IDFA transmitted to analytics service on app launch regardless of consent"` or `"DeviceInfo.getUniqueId() passed to third-party analytics API in app initialization before any consent flow"`
- **Remediation:** Even if you display an ATT prompt, reading the identifier before the user responds and transmitting it defeats the purpose of ATT and violates Apple's policy.
  1. Gate ALL identifier reads and third-party transmissions on ATT status:
     ```ts
     import { getTrackingStatus } from 'expo-tracking-transparency';
     const status = await getTrackingStatus();
     if (status === 'authorized') {
       const advertisingId = await getAdvertisingId();
       analytics.identify(advertisingId);
     } else {
       // Use anonymous session ID only
     }
     ```
  2. Audit every analytics `track()` and `identify()` call site for whether it includes a persistent device or user identifier
  3. Use ephemeral session IDs (generated fresh each app launch) for anonymous analytics instead of device-persistent IDs

## External references

- cwe CWE-359
- gdpr Art.7
- ccpa §1798.120
- owasp A04

Taxons: privacy-consent, regulatory-conformance

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