Skip to main content

Validate a device

This performs the full attestation flow:
  1. Requests a one-time challenge from the server
  2. Generates or retrieves an attestation key via Apple’s App Attest
  3. Creates an attestation object on-device
  4. Sends it to the Grantiva server for validation
  5. Returns a signed JWT with device intelligence

AttestationResult

DeviceIntelligence

Token caching

The SDK automatically caches valid tokens. Calling validateAttestation() multiple times reuses the cached token until it expires, then performs a fresh attestation.

Refresh tokens

Check if the current token is still valid, and refresh if expired:
Returns nil if no token has been stored yet (call validateAttestation() first).

Check token status

Clear stored data

Force a fresh attestation on the next call:
This clears cached keys and tokens from the Keychain. Useful for testing or user logout.

Use the token

Send the JWT to your backend as a Bearer token:
Your backend can decode the JWT to read:
  • Device risk score
  • Jailbreak detection status
  • Device model and OS version
  • Custom claims configured in the dashboard

Challenges

Every attestation starts with a one-time challenge from the server. The SDK requests it automatically, but understanding the rules helps when debugging:
  • Challenges are valid for 5 minutes. A slow attestation flow (e.g. paused in the debugger) can outlive its challenge — the server returns challenge_expired and the SDK requests a fresh one.
  • Challenges are single-use. Replaying a challenge returns challenge_already_used. Never cache or share challenges between attestation attempts.
  • The clientDataHash sent to Apple must be the SHA256 of the exact challenge string. A mismatch surfaces as client_data_hash_invalid.

Troubleshooting

Common causes when the server rejects an attestation (apple_validation_failed):
  1. Bundle ID / Team ID mismatch — the values in the request don’t match what App Attest signed against. Check that the SDK’s teamId matches your Apple Developer Team ID and the build’s bundle identifier is registered in the dashboard.
  2. Running in the Simulator — App Attest only works on real hardware. Use API key mode for Simulator and CI builds.
  3. Reused or tampered attestation object — attestation objects are single-use and bound to their challenge.
  4. Key state drift (reattest_required, HTTP 409) — the device’s stored App Attest key no longer matches the server’s records (backup restore, device transfer). The SDK self-heals by clearing local key state and re-attesting; you’ll only see the error if that retry also fails.
  5. MAD limit reached (HTTP 402) — new device onboarding is paused because your plan’s Monthly Active Device limit was exceeded and the grace period ended. See Billing.

Error handling

See Error Handling for the full list of error types.

Backend verification

See the Backend JWT Verification guide for examples of verifying the token server-side in Node.js, Python, and Go, including JWKS fetching and risk-based access control.