#devicecheck
★ Just published: Proxed AI — Secure AI API management for iOS apps with DeviceCheck

A secure proxy service that protects AI API keys using Apple DeviceCheck, enabling safe integration of ChatGPT, Claude, and other AI models in iOS apps
Proxed AI: Secure AI API management for iOS apps with DeviceCheck
A secure proxy service that protects AI API keys using Apple DeviceCheck, enabling safe integration of ChatGPT, Claude, and other AI models in iOS apps
openalternative.co
September 24, 2025 at 2:00 PM
macOS 14 *finally* supports device attestation during MDM enrollment, making it possible to ensure that you're only providing certs to an authorised device. This, naturally, coincides with us deploying a terrible workaround using DeviceCheck.
June 5, 2023 at 8:58 PM
Had an absolute blast at @serversideswift.info this week!

I’m very glad that I didn’t realise that folks who built the App Attest framework covered by my talk were in the audience until AFTER my talk 👀😅 (thanks for the photo @tracymiranda.bsky.social!)
October 4, 2025 at 9:29 AM
Protecting APIs and LLMs from fraud requires verifying genuine Apple devices.

Arsh explains DeviceCheck, App Attest, and how Firebase App Check puts them to work:

go.peterfriese.dev/firebase-de...
DeviceCheck and App Attest: Stopping Fraud in iOS Apps
Your API has no idea what is calling it. A request arriving at POST /api/redeem looks identical...
dev.to
September 2, 2026 at 9:30 AM
@oliverbinns.co.uk gave us a deep dive into Apple’s DeviceCheck technology and app security, and @beccais.online inspired us with her journey from Xcode to an App Store feature
November 25, 2024 at 10:13 AM
iOS App Attestation (DeviceCheck) is so cool. So many possibilities!

Not bullet proof but definitely a great tool for the tool belt.
December 11, 2024 at 12:46 AM
2024.6 Speaker #1: @oliverbinns.co.uk
Topic: Check Yo’ Device

Oliver will discuss Apple’s DeviceCheck technology, covering how to use it to improve app security and user experience. Learn best practices, common pitfalls, and ways to protect against fraud and unauthorized access.
November 19, 2024 at 12:23 PM
Thanks!

Pretty sure I advised you against the DeviceCheck hack, but still in awe you managed to make it work.
(Congrats to @brandonweeks.com for https://datatracker.ietf.org/doc/draft-acme-device-attest/00/ which a coworker excitedly pointed me at without knowing that we'd worked together)
June 6, 2023 at 3:02 AM
Ah I read too quickly when I responded, I was thinking like Devicecheck on the device level....
May 8, 2025 at 10:59 PM
Device Check on server-side Swift: You need the private key from App Store Connect, not embedded in your app. The app uses DeviceCheck framework client-side, your Vapor/Hummingbird server validates tokens server-side with the .p8 key. #swiftlang #serversideSwift2025
Validating apps that connect to your server | Apple Developer Documentation
Verify that connections to your server come from legitimate instances of your app.
developer.apple.com
October 3, 2025 at 5:55 PM
Next up at Do iOS is Peter Kurzok with a talk on “DeviceCheck - Securing your App’s Communication” #doios
November 12, 2025 at 9:52 AM
How do we know it really is our users talking to our APIs? Oliver Binns has an answer! He'll explain how App Attest and Device Check works and how you can use it to verify incoming requests!

Speaker Profile - www.serversideswift.info/speakers/oli...
Tickets - www.serversideswift.info/tickets
September 10, 2025 at 5:00 PM
App developers can minimize fraud by using App Attest and DeviceCheck, two tools provided by Apple. Here's how to use them to prevent unauthorized modifications to your app, and to prevent users from illegitimately acquiring premium content.
How to mitigate fraud on iOS devices using App Attest and DeviceCheck
App developers can minimize fraud by using App Attest and DeviceCheck, two tools provided by Apple. Here's how to use them to prevent unauthorized modifications to your app, and to prevent users from illegitimately acquiring premium content.
appleinsider.com
July 9, 2024 at 5:09 AM
Many commenters pointed out the irony of an EU digital ID wallet that requires Google and Apple's proprietary safety services to work. The core complaint is that using Play Integrity or DeviceCheck for remote attestation locks out anyone running alternative OSs like GrapheneOS. 1/4
June 30, 2026 at 8:00 PM
How do you know that it's your app talking to your API? There are a few techniques you check and Oliver Binns explains to us how!

youtu.be/o8nR-35Om8g
Protecting APIs with DeviceCheck, App Attest & WebAuthn - Oliver Binns
🎥 Recorded at the ServerSide.swift conference in London in 2025. 🙌 Sponsored by Broken Hands: https://www.brokenhands.io 🐥 Twitter: https://twitter.com/swiftserverconf 🐘 Mastodon:…
youtu.be
November 12, 2025 at 4:03 PM
🥈 DeviceCheck and App Attest: Stopping Fraud in iOS Apps
🔗 go.peterfriese.dev/firebase-de...

🥉 OpenLogi
🔗 go.peterfriese.dev/tools-openl...

Delivered every Tuesday to 2,000+ Apple devs.
Subscribe free 🗞️ 👉 peterfriese.dev/newsletter
Not only Swift - Newsletter archive
Archive of all newsletter issues
peterfriese.dev
September 7, 2026 at 10:05 PM
DeviceCheck and App Attest: Stopping Fraud in iOS Apps
Your API has no idea what is calling it. A request arriving at `POST /api/redeem` looks identical whether it came from your app on a real iPhone, from a modified build running on a jailbroken device, or from a Python script someone wrote after reading your traffic in Charles Proxy. HTTPS proves the connection is encrypted. It proves nothing about the client. Apple gives you two frameworks to close that gap. This post covers what each one actually guarantees, what it does not, and how to implement both without the mistakes that show up in most tutorials. ## Table of contents * What DeviceCheck actually does * What App Attest actually does * Choosing between them * Implementing DeviceCheck * Implementing App Attest * Fraud scenarios * Mistakes to avoid ## What DeviceCheck actually does DeviceCheck (iOS 11+) does two things, and it is worth being precise because it is routinely oversold. **One.** It confirms a token came from a genuine Apple device that has your app installed, where your app is tied to your developer account. **Two.** It gives you two bits of storage per device, per developer, held on Apple's servers. Two bits means four states. It survives app deletion and reinstall, which is the entire point. That's it. Read the list again for what is _not_ on it. DeviceCheck does not detect jailbreaks. It does not tell you whether your app binary was modified. It does not identify the user. It is not authentication. The two-bit storage is the interesting part. Because it persists across reinstalls, it answers questions that a locally-stored flag cannot: * Has this device already claimed the new-user promo? * Has this device been flagged for chargeback fraud? A user who deletes your app, reinstalls it, and creates a fresh account still hits the same two bits. One gotcha: the device token from `generateToken` is **single-use**. You should treat the token you receive in the completion block as single-use — although it remains valid long enough to retry a specific request, you should not use it multiple times. Generate a fresh one per request. ## What App Attest actually does App Attest (iOS 14+) answers a harder question: _is this specific request coming from an unmodified copy of my app?_ It works by generating a key pair in the device's Secure Enclave. The private key never leaves the hardware and cannot be extracted. Apple then issues a certificate chain vouching that this key belongs to a legitimate instance of your app, with your Team ID and Bundle ID baked in. Once that key is attested, your app signs each sensitive request with it. Your server verifies the signature. A tampered app cannot produce valid signatures, because it cannot get its own key attested. The flow splits into two phases, and conflating them is the most common implementation error. **Attestation** happens once per key install. Apple's servers are involved. It is relatively expensive. **Assertion** happens per request afterward. It is cheap. Critically, the assertion flow is simpler than attestation, as the Apple servers are no longer involved — your server does the verification with the public key it stored during attestation. There is no Apple REST endpoint that validates attestations or assertions for you. Your server, not the app, must validate attestations — a compromised client cannot be trusted to validate its own integrity. You parse the CBOR attestation object and check the certificate chain against Apple's App Attest root CA yourself, or you use a library that does it. ## Choosing between them | DeviceCheck | App Attest ---|---|--- Confirms genuine Apple device | Yes | Yes Confirms app binary unmodified | No | Yes Persistent per-device state | Yes (2 bits) | No Secure Enclave keys | No | Yes Replay protection | No | Yes (counter + nonce) Minimum iOS | 11 | 14 Apple servers in the loop | Every call | Attestation only Server work | Call Apple's API | Verify crypto yourself They solve different problems and compose well. App Attest tells you the request is authentic. DeviceCheck remembers that this device already burned its free trial. Plenty of production apps run both. ## Implementing DeviceCheck ### Client import DeviceCheck func fetchDeviceToken() async throws -> String { guard DCDevice.current.isSupported else { throw DeviceCheckError.unsupported } let token = try await DCDevice.current.generateToken() return token.base64EncodedString() } `isSupported` returns `false` on the Simulator. Plan for that in your dev workflow. ### Server You need a `.p8` key with DeviceCheck enabled from the Apple Developer portal, and you sign an ES256 JWT with it. The HTTP header field in each request must contain the authentication key you receive from Apple in a JSON web token. Three endpoints, all POST: * `validate_device_token` — is this a real device? * `query_two_bits` — read the stored state * `update_two_bits` — write it Swap `api.devicecheck.apple.com` for `api.development.devicecheck.apple.com` in dev. import jwt from 'jsonwebtoken'; import { randomUUID } from 'crypto'; function appleJWT() { return jwt.sign({}, PRIVATE_KEY_P8, { algorithm: 'ES256', keyid: KEY_ID, // 10-char Key ID issuer: TEAM_ID, // 10-char Team ID }); } async function queryBits(deviceToken) { const res = await fetch( `https://${HOST}/v1/query_two_bits`, { method: 'POST', headers: { Authorization: `Bearer ${appleJWT()}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ device_token: deviceToken, transaction_id: randomUUID(), timestamp: Date.now(), // milliseconds, not seconds }), } ); // A device with no bits set yet returns a plain-text body, // not JSON. Handle it before parsing. const text = await res.text(); if (text.includes('Failed to find bit state')) { return { bit0: false, bit1: false, isNew: true }; } return { ...JSON.parse(text), isNew: false }; } Two things bite people here: `timestamp` is in **milliseconds** , and `transaction_id` must be unique per request. A brand-new device returns a non-JSON body, so parse defensively. ## Implementing App Attest ### Phase 1: attestation (once) import DeviceCheck import CryptoKit func attest() async throws { let service = DCAppAttestService.shared guard service.isSupported else { throw AttestError.unsupported } // 1. Server issues a one-time random challenge. let challenge = try await api.fetchChallenge() // 2. Create a Secure Enclave key. Persist the ID in Keychain. let keyId = try await service.generateKey() try Keychain.store(keyId, for: "appattest.keyId") // 3. Hash the challenge. This must be SHA256. let clientDataHash = Data(SHA256.hash(data: Data(challenge.utf8))) // 4. Attest. Apple's servers participate in this step. let attestation = try await service.attestKey(keyId, clientDataHash: clientDataHash) // 5. Ship it to your server for verification. try await api.verifyAttestation( keyId: keyId, challenge: challenge, attestation: attestation.base64EncodedString() ) } Persist that `keyId`. If you lose it you must attest a fresh key, and each call to `generateKey` returns a new keyId referring to a unique key pair — the old ones do not get replaced. Your server then verifies the attestation object. Do not write the CBOR and X.509 parsing yourself unless you have a reason to; use `node-app-attest`, `appattest-checker-node`, or `veehaitch/devicecheck-appattest` for JVM. import { verifyAttestation } from 'node-app-attest'; const { keyId, publicKey, receipt } = verifyAttestation({ attestation: Buffer.from(req.body.attestation, 'base64'), challenge: storedChallenge, // the one YOU issued keyId: req.body.keyId, bundleIdentifier: 'com.example.app', teamIdentifier: 'ABCDE12345', allowDevelopmentEnvironment: !isProd, }); // Store publicKey for assertions, receipt for the fraud metric, // and signCount starting at 0. await db.saveAttestation({ keyId, publicKey, receipt, signCount: 0 }); Burn the challenge after use. A reused challenge defeats the whole mechanism. ### Phase 2: assertion (every sensitive request) func signedRequest(payload: [String: Any]) async throws -> Data { let keyId = try Keychain.read("appattest.keyId") let challenge = try await api.fetchChallenge() var body = payload body["challenge"] = challenge let bodyData = try JSONSerialization.data(withJSONObject: body) let clientDataHash = Data(SHA256.hash(data: bodyData)) let assertion = try await DCAppAttestService.shared .generateAssertion(keyId, clientDataHash: clientDataHash) // Send bodyData AND the assertion AND the keyId. // The assertion object does not contain the keyId. return try await api.send(body: bodyData, assertion: assertion, keyId: keyId) } Server side, no Apple call: const stored = await db.getAttestation(req.body.keyId); const { signCount } = verifyAssertion({ assertion: Buffer.from(req.body.assertion, 'base64'), payload: req.rawBody, publicKey: stored.publicKey, bundleIdentifier: 'com.example.app', teamIdentifier: 'ABCDE12345', }); // The counter must strictly increase. A repeat or a // decrease means someone is replaying a captured assertion. if (signCount <= stored.signCount) { return res.status(401).json({ error: 'replay detected' }); } await db.updateSignCount(req.body.keyId, signCount); The authenticator data includes an ever-increasing counter — checking it is what gives you replay protection, and skipping the check throws that away. ### The fraud metric That `receipt` you stored is worth something. The fraud metric is an approximate 30-day count of unique attested keys associated with your app on a particular device, retrieved by your server from the App Attest data server using a stored attestation receipt. A device generating hundreds of keys is very likely an attestation broker feeding a bot farm. A device with two or three has probably just reinstalled the app. ## Fraud scenarios **Bot traffic and credential stuffing.** A script hitting your API cannot produce an assertion, because it has no Secure Enclave key and cannot get one attested. App Attest turns "rate limit the abuse" into "the abuse cannot reach the endpoint." **Simulator and emulator abuse.** Both frameworks report `isSupported == false` on the Simulator. Free-tier farming through simulators stops working. **Repeat promo abuse.** DeviceCheck's bits outlive app deletion. Set bit0 when the promo is claimed; check it before granting. **Modified builds.** Someone patches your IPA to skip the receipt validation and sideloads it. The modified app's key will not attest, because the attestation includes a hash of your app's identity that Apple signs. **Account takeover.** Attestation gives you a stable per-install identity independent of credentials. Stolen password plus unrecognized attested key equals a good reason to demand a second factor. ## Mistakes to avoid **Treating App Attest as a boolean.** Do not reject every new key for an existing user — app reinstall and device restore can legitimately invalidate a key and require key rotation. Legitimate users rotate keys. Feed the signal into a risk score; do not hard-block on it. **Validating on the client.** If the app decides whether its own attestation is valid, an attacker patches out the check. Verification belongs on the server, always. **Reusing challenges.** One challenge, one use, short expiry, stored server-side. Otherwise a captured assertion replays forever. **Skipping the counter check.** It is three lines and it is the difference between replay protection and none. **Attesting during checkout.** Perform attestation outside critical user flows when possible, retry later on failures, and use exponential backoff instead of hard-coded retry loops. Attestation can fail transiently. Do it at launch or during onboarding, not while the user is trying to pay. **Shipping the`.p8` in the app.** Your DeviceCheck key lives on the server. Nowhere else. **Assuming this replaces everything else.** Attestation raises the cost of attack; it does not make it infinite. A determined attacker with a jailbroken device and Frida can hook the call and relay valid assertions from a script. Keep your rate limiting, your behavioral analysis, and your server-side validation. ### References * App Attest documentation * Accessing and modifying per-device data
dev.to
August 24, 2026 at 11:05 AM
Google’s Play Integrity and Apple’s DeviceCheck allow apps to query the hardware directly. The OS sends a secure cryptographic token to the app server proving the device is genuine, untampered, and trusted, without revealing any personal data.
July 30, 2026 at 6:34 PM
April 25, 2023 at 2:15 AM
eIDAS 2.0's attestation layer — the part that proves your phone is uncompromised — runs through Google Play Integrity and Apple DeviceCheck. Two US companies. Two US legal jurisdictions. Neither accountable to Brussels when it matters.
June 30, 2026 at 8:14 PM
November 18, 2024 at 11:30 PM
🔐 𝘪𝘖𝘚 𝘈𝘱𝘱 𝘈𝘵𝘵𝘦𝘴𝘵 + 𝘋𝘦𝘷𝘪𝘤𝘦𝘊𝘦𝘤𝘬: 𝘉𝘶𝘪𝘭𝘥𝘪𝘯𝘨 𝘙𝘦𝘢𝘭 𝘛𝘳𝘶𝘴𝘵 by Wesley Matlock

Cryptographically verify your app is legitimate with 𝘈𝘱𝘱 𝘈𝘵𝘵𝘦𝘴𝘵 and 𝘋𝘦𝘷𝘪𝘤𝘦𝘊𝘦𝘤𝘬—underrated security tools for production apps.

#Security #iOSdev #Backend

www.wesleymatlock.com/ios-app-atte...
iOS App Attest + DeviceCheck: Building Real Trust Into Your App (Without Losing Your Mind)
Professional iOS Engineer specializing in SwiftUI, VisionOS, and modern Apple platforms. Expert in HealthKit, tvOS, watchOS and even some VisionOS development with 50+ technical articles and…
www.wesleymatlock.com
December 3, 2025 at 2:02 PM
“[#apple’s] DeviceCheck allows developers to set and query two bits of data per device, which persist across app deletions, reinstalls, factory resets, and even device transfers between users”

From: @finnvoorhees
https://mastodon.social/@finnvoorhees/113039458899216830
August 28, 2024 at 10:17 PM
DeviceCheck é claramente um Framework pro Uber (pra resolver a treta de identificarem devices) https://developer.apple.com/documentation/devicecheck
DeviceCheck | Apple Developer Documentation
Reduce fraudulent use of your services by managing device state and asserting app integrity.
developer.apple.com
February 11, 2025 at 2:18 AM