
A contractor left your team in March. Nobody revoked their App Store Connect API key. This week that key is still valid, still scoped to App Manager, and still capable of pushing a build to every one of your 10,000 external testers. That’s the actual attack surface behind the “TestFlight Update — View Release Notes” line item that scrolled past you in the developer feed on August 25.
The release itself (08252026a) is a routine TestFlight app update — stability fixes plus support for testing builds compiled against the current beta SDKs. Worth installing, not worth a meeting. But it landed the same week iOS 27 and macOS 27 developer betas started moving through TestFlight in volume, and that’s a decent moment to look at the identity layer nobody audits: who can actually distribute your betas, and with what credential.
What actually shipped, and the version floor
The TestFlight client requires iOS 16 or later, and the August build is what lets testers install and run apps built against the iOS 27 / iPadOS 27 / macOS 27 / watchOS 27 SDKs now in beta. If your testers are on the iOS 27 seed and pulling your builds, they need this update or they’ll hit install failures on newer builds. That’s the whole practical story for testers.
The more interesting change is upstream, in App Store Connect, where TestFlight actually gets managed. Two things matter to anyone running a beta program at scale right now:
- TestFlight tester criteria, introduced at WWDC 2025 and now fully rolled out, lets you target beta builds by device model and OS version instead of blasting every build to every tester. You can also send a public link that only enrolls people who match. Fewer wasted installs, fewer “why is my iPhone 12 crashing on your Vision-only feature” tickets.
- App Store Connect API is the only credential you should be using for automated distribution — and it’s the piece most teams get wrong.
The credential model: an ES256 key, not a password
Uploading and managing TestFlight builds through CI runs on the App Store Connect API, which authenticates with a JSON Web Token you sign yourself using an Elliptic Curve P-256 (ES256) private key issued in App Store Connect. There is no server round-trip to get the token — you mint it locally, it’s valid for up to 20 minutes, and Apple validates the signature against the public key tied to your key ID. That’s a clean, modern design. It’s also a design where the .p8 private key is the identity, and Apple will only let you download it once.
Here’s what a correct token mint looks like — note the 20-minute cap and the exact audience string, both of which Apple enforces:
import jwt # PyJWT
import time
KEY_ID = "2X9R4HXF34"
ISSUER_ID = "57246542-96fe-1a63-e053-0824d011072a"
PRIVATE_KEY = open("AuthKey_2X9R4HXF34.p8").read()
now = int(time.time())
token = jwt.encode(
{
"iss": ISSUER_ID,
"iat": now,
"exp": now + 20 * 60, # hard 20-min ceiling; longer is rejected
"aud": "appstoreconnect-v1",
},
PRIVATE_KEY,
algorithm="ES256", # P-256 only; RS256/HS256 are rejected
headers={"kid": KEY_ID, "typ": "JWT"},
)
print(token)
With that token, listing your beta testers or groups is a plain REST call:
curl -s -H "Authorization: Bearer $TOKEN" \
"https://api.appstoreconnect.apple.com/v1/betaGroups?limit=200" \
| jq '.data[] | {name: .attributes.name, public: .attributes.isInternalGroup}'
The concrete improvement over the old world is worth naming: this replaced Apple ID username plus app-specific password in altool, which meant CI systems held a credential that could touch far more than builds and couldn’t be scoped. API keys carry a role — Admin, App Manager, Developer, Marketing, Customer Support — and as of the current App Store Connect you can create Individual Keys tied to a single person’s access rather than team-wide keys. Use those. A team-wide App Manager key on a CI runner is a standing liability; an individual key scoped to Developer that only uploads builds is not.
Managed Apple IDs are eating the developer account
Here’s the adjacent shift enterprise admins should be building toward. The Apple Developer Program and App Store Connect access now federate through Managed Apple Accounts in Apple Business Manager, which in turn federate to Entra ID, Okta, or Google Workspace. That means your App Store Connect team members — the people who can ship a TestFlight build — can be governed by your IdP: SCIM-provisioned, MFA-enforced, and de-provisioned the moment HR flips the switch in your directory.
If your developer team is still a pile of personal Apple IDs with app-specific passwords, that contractor from the opening paragraph is exactly why you migrate. Federated Managed Apple Accounts close the offboarding gap that API keys alone don’t — because a human leaving your directory should also lose their ability to mint keys in the first place.
The order of operations, roughly:
- Verify and claim your domains in Apple Business Manager, then set up federated authentication to your IdP under Settings → Federated Authentication.
- Provision developer team members as Managed Apple Accounts via SCIM.
- Assign App Store Connect roles per person; issue individual API keys, not team keys, for automation.
- Rotate any legacy team-wide keys and delete app-specific passwords.
The fleet side: TestFlight on managed devices is a policy decision
TestFlight installs unreviewed beta code. On a supervised corporate fleet, that’s often something you want off by default and enabled per-team by exception. You block it with an application-access restriction that denies the TestFlight bundle ID:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadType</key>
<string>com.apple.applicationaccess</string>
<key>PayloadIdentifier</key>
<string>com.example.mdm.restrict-testflight</string>
<key>PayloadUUID</key>
<string>8B0E9C6A-2F41-4E7D-9A3C-1D5E7F0B2C44</string>
<key>PayloadVersion</key>
<integer>1</integer>
<!-- Deny beta app distribution on supervised devices -->
<key>blacklistedAppBundleIDs</key>
<array>
<string>com.apple.TestFlight</string>
</array>
</dict>
</plist>
Separately, if you don’t want employees enrolling corporate devices in Apple’s OS beta seed (a different problem from app betas), manage that through declarative device management. A software-update enforcement declaration that pins a target OS version keeps devices off unvetted iOS 27 seeds until you’ve qualified them, which matters when a beta ships a Secure Enclave or passkey-sync regression you haven’t tested against your MDM stack.
Verify it in five minutes
- Key hygiene: App Store Connect → Users and Access → Integrations → App Store Connect API. Every key should map to a named owner and the minimum role. Anything you can’t explain, revoke.
- Token sanity: run the JWT mint above and hit
/v1/apps— a 401 means a badkid, wrong audience, or an expired/exceededexp. A 200 with your app list means the key works and is live. - Federation: in ABM, confirm developer team members show as federated Managed Apple Accounts, not personal Apple IDs, and test that IdP de-provisioning revokes access.
- Fleet: push the restriction profile to a test device and confirm TestFlight disappears from the Home Screen.
Bottom line
- Install the TestFlight update — testers on iOS 27 betas need it to receive new builds.
- The real work is the credential behind distribution: move to individual, least-privilege App Store Connect API keys and kill team-wide keys and app-specific passwords.
- Federate developer-team access through Managed Apple Accounts and your IdP so offboarding actually revokes the ability to ship builds.
- Decide TestFlight and OS-beta policy for your managed fleet on purpose, not by default — block the bundle ID and pin OS versions where you need control.