Skip to Content
App AttestationOverview

App attestation

App attestation proves to your backend that a request comes from an unmodified build of your app running on a genuine device that meets your policy. Included in every TrustPin plan at no extra charge.

This page is for security leads and everyone starting with attestation: what it proves, who runs what, and the path to a first verified token.

It is not runtime protection (RASP): it doesn’t detect hooking, debugging, tampering, root or jailbreak. See What attestation is not.

Who runs what

TrustPin operates no attestation service. Everything in the request path runs in your infrastructure. TrustPin’s only runtime role is serving the signed policy (attest.jws.b64) from its CDN.

ComponentRuns atRole
TrustPin console and CLITrustPin, your workstationSet up, edit, sign and publish the attestation policy; generate the deployment bundle
TrustPin CDNTrustPinServes the signed policy to your verifier
Attestation verifierYour infrastructure (container image or binary)Verifies Apple App Attest, Android Key Attestation and Play Integrity evidence against the policy; issues short-lived tokens
SDK attestation moduleYour appProduces evidence, obtains and renews tokens, adds the TrustPin-Attestation header to requests
Your API or gatewayYour infrastructureVerifies tokens against the verifier’s JWKS and decides what unattested requests may do

TrustPin stores identifiers only: Team IDs, bundle IDs, package names, signing-certificate digests, the Google Cloud project number. It never holds your verifier’s signing keys, challenge secrets or Google credentials; those are environment variables of your verifier.

What TrustPin sees

TrustPin sees no attestation traffic. From your verifiers’ policy fetches it records:

  • request counts, split into full fetches and “not modified” answers;
  • verifier versions and the operating system and architecture they run on;
  • the CDN location that served the fetch;
  • counts of distinct verifier server IP addresses.

None of this is device or end-user data. You see it on the project’s Analytics page (“Attestation verifiers”), the Usage pages (“Attestation policy fetches”) and the Status tab of the project’s Attestation page (“Verifier fleet”).

The flow

Platform evidence is expensive (an Apple or Google round trip, subject to quotas), so it is produced once per device key. Tokens are then renewed cheaply by signing a fresh challenge with that key.

1. Attest (once per device key) app ── GET /v1/challenge ───────────────────────▶ verifier app ── POST /v1/attest {clientData, device key, evidence} ▶ verifier ── Apple / Google app ◀─ { keyCertificate, token } ─────────────────── 2. Refresh (before the token expires, or when the binding changes) app ── GET /v1/challenge ───────────────────────▶ verifier app ── POST /v1/token {keyCertificate, clientData, assertion} ▶ verifier app ◀─ { token } ─────────────────────────────────── 3. Use app ── API request + TrustPin-Attestation: <token> ▶ your API (verifies via JWKS)

The device key is the App Attest key on Apple platforms (Secure Enclave) and an Android Keystore key on Android. A key is attested exactly once; when its key certificate expires or is rejected, the SDK generates a new key and attests again. The Key certificate lifetime in the policy (default 30 days) therefore controls how often a device attests again.

For an operation your API must not accept on a reused token alone, SDK 7.1.0 and later can add an assertion bound to that operation. See Assertions for sensitive operations.

Token binding

A token can be bound to your user’s session, so a token copied out of one session is useless in another. Only a SHA-256 of the value leaves the device, and your API compares it with the session the request authenticates with.

The app chooses one of two modes: header binding, where each request’s token is bound to a header such as Authorization, or fixed binding, to a value you set. With Require token binding on, tokens exist only once the app has a binding.

Modes

ModeVerifier behaviourUse
MonitorIssues a token even when policy checks fail, recording tp.result fail and tp.reasons. An app the policy doesn’t list gets 403 app_not_allowed in both modesRoll-out: your API logs tp.result and allows every request
EnforceIssues tokens only when the policy passes. Tokens issued before a switch to enforce keep fail until they expire; see Rolling outProduction: your API rejects requests without a valid token

Cryptographic failures (forged evidence, invalid signatures) never yield a token in either mode.

Platforms

SDK 7.1.0 and later.

PlatformEvidenceMinimum OS
iOS, iPadOSApp Attest15
macOS, Mac CatalystApp Attest13, 15
tvOSApp Attest15
watchOSApp Attest9
visionOSApp Attest2
Android with Google Play servicesPlay Integrity, Key Attestation or bothAPI 25
Android without Google PlayKey AttestationAPI 25
Simulators and emulatorsDebug token (development only)Not applicable

Where a device cannot produce the evidence the policy requires, the SDK reports it and sends requests without the header; your API decides how to treat them.

Setting it up

  1. Apps and policy. On the project’s Attestation page, add your apps (Team ID and Bundle ID on Apple; Package name and Signing certificate SHA-256 on Android), choose the mode and token settings, then Sign and publish. Running verifiers pick up a new version within their refresh interval (5 minutes by default). See Setting up apps and the policy.
  2. Verifier. Deploy it from the console’s deployment bundle. See Deploying the verifier.
  3. Pinning. Register the verifier’s domain in your TrustPin project, because the SDKs pin their calls to it. See Setting up apps and the policy.
  4. App. Add the attestation module and point it at your verifier. See iOS and macOS or Android. Flutter and React Native are coming.
  5. API. Verify the TrustPin-Attestation header. See Verifying tokens in your API or Checking tokens at the gateway.
  6. Roll out. Measure in monitor mode, then enforce. See Rolling out.

Projects signed with FIDO can’t use app attestation in this release.

Next steps