device-integrity
This Claude Code skill provides Swift implementations for Apple's DeviceCheck and App Attest frameworks to verify that server requests originate from legitimate Apple devices running genuine app instances. It includes token generation patterns, Secure Enclave key management for App Attest, attestation and assertion flows, and guidance for server-side verification with Apple's APIs. Use this when building fraud prevention systems, protecting sensitive endpoints, validating app authenticity, or implementing device verification in backend architectures.
git clone --depth 1 https://github.com/dpearson2699/swift-ios-skills /tmp/device-integrity && cp -r /tmp/device-integrity/skills/device-integrity ~/.claude/skills/device-integritySKILL.md
# Device Integrity
Verify that requests to your server come from a genuine Apple device running a
legitimate instance of your app. DeviceCheck provides per-device bits for
simple flags (e.g., "claimed promo offer"). App Attest uses Secure Enclave keys
and Apple attestation to cryptographically prove app legitimacy on sensitive
requests.
## Contents
- [DCDevice (DeviceCheck Tokens)](#dcdevice-devicecheck-tokens)
- [DCAppAttestService (App Attest)](#dcappattestservice-app-attest)
- [App Attest Key Generation](#app-attest-key-generation)
- [App Attest Attestation Flow](#app-attest-attestation-flow)
- [App Attest Assertion Flow](#app-attest-assertion-flow)
- [Server Verification Guidance](#server-verification-guidance)
- [Error Handling](#error-handling)
- [Common Patterns](#common-patterns)
- [Common Mistakes](#common-mistakes)
- [Review Checklist](#review-checklist)
- [References](#references)
## DCDevice (DeviceCheck Tokens)
[`DCDevice`](https://sosumi.ai/documentation/devicecheck/dcdevice) generates a
unique, ephemeral token that identifies a device. Treat each token as
single-use: generate a new token for each server operation instead of caching or
reusing one. The token is sent to your server, which then communicates with
Apple's servers to read or set two per-device bits. Available on iOS 11+.
### Token Generation
```swift
import DeviceCheck
func generateDeviceToken() async throws -> Data {
guard DCDevice.current.isSupported else {
throw DeviceIntegrityError.deviceCheckUnsupported
}
return try await DCDevice.current.generateToken()
}
```
### Sending the Token to Your Server
```swift
func sendTokenToServer(_ token: Data) async throws {
let tokenString = token.base64EncodedString()
var request = URLRequest(url: serverURL.appending(path: "verify-device"))
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONEncoder().encode(["device_token": tokenString])
let (_, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw DeviceIntegrityError.serverVerificationFailed
}
}
```
### Server-Side Overview
The server exchanges each fresh token with Apple's authenticated DeviceCheck API.
Load [DeviceCheck Server Endpoints](references/device-integrity-patterns.md#devicecheck-server-endpoints)
for endpoint and environment details.
### What the Two Bits Are For
Apple stores two Boolean values per device per developer team. You decide what
they mean. Common uses:
- **Bit 0:** Device has claimed a promotional offer.
- **Bit 1:** Device has been flagged for fraud.
Bits persist across app reinstall. You control when to reset them via the
server API.
## DCAppAttestService (App Attest)
[`DCAppAttestService`](https://sosumi.ai/documentation/devicecheck/dcappattestservice)
validates that a specific instance of your app on a specific device is
legitimate. It uses a hardware-backed key in the Secure Enclave to create
cryptographic attestations and assertions. Available on iOS 14+.
The flow has three phases:
1. **Key generation** -- create a key pair in the Secure Enclave.
2. **Attestation** -- Apple certifies the key belongs to a genuine Apple device running your app.
3. **Assertion** -- sign server requests with the attested key to prove ongoing legitimacy.
### Checking Support
```swift
import DeviceCheck
let attestService = DCAppAttestService.shared
guard attestService.isSupported else {
// Fall back to DCDevice token or other risk assessment.
// App Attest is not available on simulators or all device models.
return
}
```
For app extensions, App Attest is supported only in Action, extensible SSO, and
watchOS extensions. Treat other extension types as unsupported even if
`isSupported` returns `true`.
## App Attest Key Generation
Generate one cryptographic key pair per user account on each device. The
private key stays in the Secure Enclave. The returned `keyId` is the only
identifier your app can later use to access the key, so record and reuse the
account/device-scoped `keyId`; do not share one key across users. Avoid
unnecessary regeneration because each new key affects App Attest key-count risk
metrics. Only treat the `keyId` as usable after your server verifies
attestation. If server verification fails, discard the `keyId` and generate a
new key before retrying.
```swift
import DeviceCheck
actor AppAttestManager {
private let service = DCAppAttestService.shared
private var keyId: String?
/// Generate and record a key pair for App Attest.
func generateKeyIfNeeded() async throws -> String {
if let existingKeyId = loadKeyIdFromKeychain() {
self.keyId = existingKeyId
return existingKeyId
}
let newKeyId = try await service.generateKey()
saveKeyIdToKeychain(newKeyId)
self.keyId = newKeyId
return newKeyId
}
// MARK: - Keychain helpers (simplified)
private func saveKeyIdToKeychain(_ keyId: String) {
let data = Data(keyId.utf8)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "app-attest-key-id-\(currentAccountID)",
kSecAttrService as String: Bundle.main.bundleIdentifier ?? "",
kSecValueData as String: data,
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
]
SecItemDelete(query as CFDictionary) // Remove old if exists
SecItemAdd(query as CFDictionary, nil)
}
private func loadKeyIdFromKeychain() -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "app-attest-key-id-\(currentAccountID)",
kSecAttrService as String: Bundle.main.bundleIdenDiscover and configure Bluetooth and Wi-Fi accessories using AccessorySetupKit. Use when presenting a privacy-preserving accessory picker, defining discovery descriptors for BLE or Wi-Fi devices, handling accessory session events, migrating from CoreBluetooth permission-based scanning, or setting up accessories without requiring broad Bluetooth permissions.
Implement, review, or improve Live Activities and Dynamic Island experiences in iOS apps using ActivityKit. Use when building real-time updating widgets for the Lock Screen and Dynamic Island — delivery tracking, sports scores, ride-sharing status, workout timers, media playback, or any time-sensitive information that updates in real time. Also use when working with ActivityKit, ActivityAttributes, Activity lifecycle (request/update/end), Dynamic Island layouts (compact/minimal/expanded), push-to-update Live Activities, or Lock Screen live widgets.
Measure ad effectiveness with privacy-preserving attribution using AdAttributionKit. Use when registering ad impressions, handling attribution postbacks, updating conversion values, implementing re-engagement attribution, configuring publisher or advertiser apps, or replacing SKAdNetwork with AdAttributionKit for ad measurement.
Implement AlarmKit alarms and countdown timers for iOS and iPadOS with Lock Screen, Dynamic Island, StandBy, and paired Apple Watch system UI. Covers AlarmManager scheduling, AlarmAttributes and AlarmPresentation, system Stop and AlarmButton secondary actions, authorization, state observation, countdown widget-extension handoff, and Live Activity integration. Use when building wake-up alarms, countdown timers, or alarm-style alerts that need Apple's system alarm experience.
Build iOS App Clips with invocation URLs, App Clip Codes, NFC, QR codes, Safari banners, Maps, Messages, target setup, App Store Connect experiences, size/capability constraints, NSUserActivity routing, SKOverlay promotion, App Group/keychain handoff, ephemeral notifications, location confirmation, and full-app migration. Use when creating App Clips or wiring App Clip invocation, experience configuration, or full-app handoff.
Implement App Intents for Siri, Shortcuts, Spotlight, widgets, Control Center, and Apple Intelligence on iOS. Covers AppIntent actions, AppEntity and EntityQuery models, AppShortcutsProvider phrases, IndexedEntity Spotlight indexing, WidgetConfigurationIntent, SnippetIntent, and assistant schemas. Use when exposing app actions or entities to system surfaces.
Optimize App Store product pages for search visibility and conversion. Use for App Store Optimization (ASO), keyword research, app name/subtitle/keyword-field strategy, conversion-focused descriptions and promotional text, screenshot captions and ordering, Custom Product Pages with assigned search keywords, In-App Events, Product Page Optimization tests, localized metadata, ratings/review strategy, and in-app review prompt timing with RequestReviewAction or AppStore.requestReview. Also use when routing ASO vs App Store review, privacy/ATT, or StoreKit implementation boundaries.
Audits App Store submission readiness and rejection risk across current review guidelines, PrivacyInfo.xcprivacy and required-reason APIs, privacy labels, ATT, StoreKit payments, metadata, entitlements, widgets, and Live Activities. Use when preparing a submission, responding to rejection, reconciling privacy evidence, or separating upload blockers from cleanup.