Skip to main content

App Attest

The SDK authenticates itself. Every capture and data request is authorized by a bearer the SDK mints from an Apple App Attest assertion, scoped to the organisation you name. The device is attested against your App ID — which is why this cannot be pre-configured for you, and why the SDK carries no key of its own.

Four steps, in order. Skip any of them and device auth fails with auth(_:).

1. Get registered

Send us your app's bundle identifier and Team ID. You get back a pk_ios_* publishable key per environment. The key is publishable — it ships in your binary — but it is bound to the App ID and Team we register for App Attest.

For a key, a backend registration, or questions: info@thenoughtyfox.com.

2. Enable the App Attest capability

Add the App Attest capability to your app target under Signing & Capabilities, or enable DeviceCheck / App Attest on the App ID in the developer portal. Regenerate provisioning profiles afterwards.

3. Set the environment entitlement

<key>com.apple.developer.devicecheck.appattest-environment</key>
<string>development</string>

development uses Apple's App Attest sandbox; production uses the production service. Keys created in one do not work in the other.

Apple ignores this entitlement once you distribute

Builds delivered through TestFlight, the App Store, or the Enterprise Program always attest against the production environment, whatever the entitlement says. Only locally-signed development builds honour development.

4. Match the environment to the key

Configuration.environment picks the SDK backend, and each backend validates attestations against one App Attest environment:

APIEnvironmentApp Attest environmentUse for
.developmentsandboxLocally-signed development builds
.productionproductionTestFlight, App Store, Enterprise

A mismatch — a TestFlight build (production attestation) pointed at .development, say — is rejected at the backend and reaches you as attestationInvalid or serverError(statusCode:).

Pick the environment from your build configuration, and use the publishable key issued for that same environment:

let configuration = Configuration(
environment: isReleaseBuild ? .production : .development,
publishableKey: "pk_ios_…",
organizationProvider: authService,
settings: settingsStore
)

Establishing device auth

try await tours.prepareDeviceAuth()

Call it after login and organisation selection (and at launch for a restored session), so a broken auth chain surfaces at a clean moment instead of on the first sync. It is independent of start().

A retryable failure here costs nothing — sync re-validates before every run. A non-retryable one (appAttestUnavailable, noOrganizationSelected, a rejected key) will never resolve on its own and is worth surfacing.

Revoking on logout

try? await tours.revokeDeviceSession()

Ends the device session and drops the local credentials — even if the server-side revoke fails, so logout can always proceed.

Keep the same NoughtyTours instance: the organisation is read live through your provider, so signing in as a different organisation needs no new one.