# Photo library permission limited to selected photos on iOS 14+

- **Pattern:** `ab-001906` (`mobile-permissions-privacy.data-handling.photo-library-limited`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001906
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001906)

## Why it matters

iOS 14 introduced photo library limited access: users can grant access to a specific selection of photos rather than their entire library. An app that requests full photo library access for a feature that only needs one photo — such as a profile picture upload — forces users to choose between granting access to their entire camera roll or denying the feature entirely. This is a GDPR Art. 5(1)(c) data-minimisation violation. Apple's App Store reviewers check whether apps use the full library permission when limited access would suffice; requesting more than necessary is grounds for a guideline violation. CWE-272 (Least Privilege Violation) applies when an app acquires more access than its operation requires.

## Severity rationale

Low because the over-permission is scoped to photo access rather than more sensitive data types, but it remains a GDPR Art. 5(1)(c) data-minimisation violation and a common App Store Review guideline failure.

## Remediation

Use `expo-image-picker` with default settings for features that need one or a few photos — it automatically uses the limited access picker on iOS 14+. Only declare `NSPhotoLibraryUsageDescription` for features that browse the full library.

```typescript
import * as ImagePicker from 'expo-image-picker'

// Profile photo upload: limited access is appropriate
async function pickProfilePhoto() {
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ImagePicker.MediaTypeOptions.Images,
    allowsEditing: true,
    aspect: [1, 1],
    quality: 0.8,
    // On iOS 14+, this presents the limited-access picker by default
  })
  if (!result.canceled) {
    uploadProfilePhoto(result.assets[0].uri)
  }
}
```

In `app.json`, only add `NSPhotoLibraryUsageDescription` if a feature genuinely browses the full library. Use `NSPhotoLibraryAddOnlyUsageDescription` for save-only operations (e.g., saving generated images). Remove `NSPhotoLibraryUsageDescription` if all photo access goes through the picker.

## Detection

- **ID:** `photo-library-limited`
- **Severity:** `low`
- **What to look for:** Count all photo library access calls (ImagePicker, photo library APIs). For each, check whether the limited permission scope is used on iOS 14+. Check `Info.plist` for `NSPhotoLibraryUsageDescription` vs `NSPhotoLibraryAddOnlyUsageDescription` entries.
- **Pass criteria:** If app accesses photo library, at least 100% of photo access calls use the limited permission (allowing users to select photos) when the feature only needs a single photo or a subset. Only full access is requested if the feature genuinely requires browsing all photos.
- **Fail criteria:** App requests full photo library access for features that could work with limited access (e.g., profile photo upload). OR no photo library permission granularity is implemented on iOS 14+.
- **Skip (N/A) when:** App does not access photo library (no photo picker or library import calls found), or targets pre-iOS 14 only.
- **Detail on fail:** Quote the permission config. `"App requests full photo library access but only needs to upload a single photo (should use limited access)"` or `"Photo library permission not differentiated for iOS 14+ (user cannot choose selected photos)"`
- **Remediation:** Use limited photo access on iOS 14+ to give users privacy control:

  ```json
  {
    "app.json": {
      "ios": {
        "infoPlist": {
          "NSPhotoLibraryUsageDescription": "Select photos to share",
          "NSPhotoLibraryAddOnlyUsageDescription": "Save photos from the app"
        }
      }
    }
  }
  ```

  ```typescript
  import * as ImagePicker from 'expo-image-picker'

  async function pickPhoto() {
    // This uses limited photo access on iOS 14+
    const result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.Images,
      allowsMultiple: false,
    })
  }
  ```

## External references

- cwe CWE-272
- gdpr Art. 5(1)(c)
- external apple-photo-limited-access — https://developer.apple.com/documentation/photokit/supporting_limited_photos_library_in_your_app

Taxons: access-control, privacy-consent

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