A comprehensive sample application demonstrating Apple's DeviceCheck API, including Device Identification and App Attest features.
This repository contains two main components:
- DeviceCheckApp: iOS test application built with SwiftUI
- DeviceCheckServer: Backend server built with Swift and Vapor
- Xcode 15.0 or later
- iOS 17.0 or later
- Swift 5.9 or later
- Swift 5.9 or later
- macOS 13.0 or later (for development)
Before running the server, you need to configure your Apple credentials.
Create a new file at DeviceCheckServer/Sources/App/PrivateConstants.swift with the following structure:
import Foundation
struct PrivateConstants {
static let authKeyPath = "./Resources/AuthKey.p8"
static let authKeyID = "YOUR_KEY_ID"
static let teamID = "YOUR_TEAM_ID"
}- Go to Apple Developer Portal - Keys
- Create a new key with DeviceCheck capability enabled
- Download the
.p8key file (⚠️ You can only download it once!) - Copy the downloaded file to
DeviceCheckServer/Resources/AuthKey.p8
Open DeviceCheckServer/Sources/App/PrivateConstants.swift and fill in your actual values:
Finding Your Credentials:
-
authKeyID(Key ID): Your 10-character Key ID- Location 1: Apple Developer Portal > Certificates, Identifiers & Profiles > Keys
- Location 2: In the filename of your downloaded .p8 file:
AuthKey_<KEY_ID>.p8 - Format: 10-character string (e.g.,
ABC123DEFG)
-
teamID(Team ID): Your 10-character Team ID- Location: Apple Developer Portal > Membership
- Format: 10-character string (e.g.,
ABCD123456)
Example with actual values:
import Foundation
struct PrivateConstants {
static let authKeyPath = "./Resources/AuthKey.p8"
static let authKeyID = "ABC123DEFG"
static let teamID = "ABCD123456"
}File Structure After Setup:
DeviceCheckServer/
├── Resources/
│ └── AuthKey.p8 # Your actual .p8 key file (NOT in git)
└── Sources/
└── App/
└── PrivateConstants.swift # Your actual config (NOT in git)
Security Notes:
- ✅
PrivateConstants.swiftis in.gitignore- your secrets won't be committed - ✅
AuthKey.p8files are in.gitignore- your keys won't be committed ⚠️ Never commit your actual credentials to the repository
Navigate to the server directory and run:
cd DeviceCheckServer
swift runThe server will start on http://localhost:8080
Health Check:
GET /- Server infoGET /health- Health status
Device Identification:
POST /api/device/query- Query device bitsPOST /api/device/update- Update device bitsPOST /api/device/validate- Validate device
App Attest:
GET /api/attest/challenge- Get attestation challengePOST /api/attest/validate- Validate attestationPOST /api/attest/assertion- Validate assertion
- Open
DeviceCheckApp/DeviceCheck.xcodeprojin Xcode - Update the bundle identifier to match your Apple Developer account
- Select a physical device (DeviceCheck requires a real device, not simulator)
- Make sure the server is running
- Build and run the app (⌘R)
Note: DeviceCheck APIs only work on physical iOS devices, not in the simulator.
To access the server from your iPhone, you need to configure the iOS app to use your Mac's local IP address instead of localhost.
- Open System Settings (or System Preferences on older macOS)
- Go to Network
- Select your active Wi-Fi connection
- Note your IP Address (e.g.,
192.168.1.100)
Alternatively, use Terminal:
ipconfig getifaddr en0(Replace en0 with your active network interface if different)
Make sure your iPhone and Mac are connected to the same Wi-Fi network.
- Open
DeviceCheckApp/DeviceCheckApp/Constants.swift - Replace
localhostwith your Mac's IP address:(Replacestatic let baseURL = "http://192.168.1.100:8080"
192.168.1.100with your actual IP address)
If your Mac's firewall blocks incoming connections:
- Open System Settings → Network → Firewall
- Click Options or Firewall Options
- Ensure incoming connections are allowed for the Vapor server, or temporarily disable the firewall for testing
If running the server on a different machine or port, update baseURL accordingly:
static let baseURL = "http://your-server-address:8080"The Device Identification tab demonstrates Apple's two-bit storage API:
- Query Request: Retrieves the current values of the two bits from Apple's servers
- Update Request: Updates the two bits with new values
- Device Validation: Validates the device against custom criteria
The UI displays the bits as gray rounded squares that show 0 or 1 when populated.
The App Attest tab is a placeholder for App Attest functionality. The server includes endpoints for:
- Challenge generation
- Attestation validation
- Assertion validation
You can use Proxyman to inspect the HTTP requests your server makes to Apple's DeviceCheck API.
- Open Proxyman
- Go to Setup → Automatic Setup
- Click Open New Terminal
- In the new Terminal window, navigate to your project and run:
cd DeviceCheckServer swift run - All HTTP/HTTPS traffic from this Terminal session will be captured by Proxyman
The server supports proxy configuration via environment variables:
cd DeviceCheckServer
USE_PROXY=true PROXY_HOST=localhost PROXY_PORT=9090 swift runEnvironment Variables:
USE_PROXY=true- Enable proxy (set to "true" to enable)PROXY_HOST- Proxy hostname (default: "localhost")PROXY_PORT- Proxy port (default: 9090)
Proxyman Default Ports:
- HTTP:
9090 - HTTPS:
9091
Note: For HTTPS traffic to Apple's API, you may need to trust Proxyman's certificate. Proxyman will guide you through this process.
Device Identification: ✅ Fully Implemented
- Real integration with Apple's DeviceCheck API
- JWT token generation with ES256 signing
- Query and update device bits functionality
- Comprehensive logging for debugging
App Attest:
- Server includes endpoints for challenge generation, attestation, and assertion validation
- Currently returns mock responses
- To fully implement:
- Add proper CBOR parsing for App Attest attestation and assertion objects
- Implement cryptographic verification of App Attest signatures
- Store public keys for assertion validation
- SwiftUI for the user interface
- MVVM architecture with view models
- DeviceCheck framework for device token generation
- App Attest framework for attestation (placeholder)
- Vapor web framework
- Controller-based routing
- Separate controllers for Device Identification and App Attest
- Models for request/response objects
For production deployment:
- Update server configuration in
configure.swift - Configure proper authentication with Apple's API
- Add database persistence for storing device states and public keys
- Implement proper error handling and logging
- Add rate limiting and security measures
- Use HTTPS for all communications
Run server tests:
cd DeviceCheckServer
swift test- App icon: Mobile Phone Icon from Flaticon
See LICENSE.txt for details.
- DeviceCheck is only available on physical iOS devices (iOS 11.0+)
- App Attest requires iOS 14.0 or later
- The two-bit storage is device-specific and persists across app installations
- Proper implementation requires server-side verification with Apple's servers
