In my Android integrity work, the hard question is not whether the app received a token. It is whether the backend can verify that the evidence belongs to the exact protected action—and then make a separate authorization decision. Valid evidence for the wrong request is useless.
Google's Play Integrity standard-request documentation describes preparing a token provider, requesting a verdict for a protected action, and passing the token to a backend for verification. This is not a client-side security gate: the backend owns the decision.
Tie the evidence to the intended action
For standard requests, Google recommends supplying a requestHash computed from the request's important fields. Compute a digest over a stable, canonical representation of the relevant operation. Both client and backend need to produce the same digest for the same action.
A model might include the operation name, request body version, authenticated principal identifier if appropriate, resource identifier, and a server-provided action identifier. Beware subtle differences in serialization order, number formats and Unicode normalization; blindly hashing arbitrary JSON strings on each side often produces mismatches.
Do not include secrets or personal payloads as plain text in requestHash. Google returns the value inside the signed integrity response, so it must be treated as visible request metadata.
What the backend must do
For standard Play Integrity requests, the backend sends the token to Google Play for decryption and verification. From the returned verdict it must validate, at minimum, the properties relevant to its policy:
- The request package name matches the expected application identity.
- The returned requestHash matches the backend-computed digest of the exact protected action.
- The token timestamp satisfies the service's freshness requirements.
- Relevant app and device verdicts meet the risk policy for the operation.
- The caller's independently authenticated session remains valid.
- That caller is authorized to operate on the requested resource.
The official verdict documentation emphasizes checking request details before applying verdict-based policy.
Why sample code can mislead
This policy sketch intentionally assumes the Play response has already been authenticated by Google's API. It is not a token decoder and should never receive untrusted booleans straight from the Android client.
Notice that the fifth check is still required when the first four succeed. Compromised credentials on an otherwise legitimate app are still compromised credentials.
Google notes that standard requests have built-in replay protections. Do not mechanically copy classic-request nonce handling or Apple's assertion-counter rules into the standard API model. The implementation and the expected semantics are platform-specific.
A practical test matrix
| Input variation | Expected policy behavior |
|---|---|
| Same verdict, different protected request | Reject request hash mismatch |
| Valid verdict, wrong package identity | Reject |
| A stale token outside policy | Reject or explicit challenge refresh |
| Legitimate app, revoked account | Reject |
| Integrity service unavailable | Risk-specific degraded policy, never a silent success |
| Valid integrity, resource owned by another user | Reject object-level authorization |
Sources and scope
The authoritative references are Google's standard request flow, verdict interpretation, and the separate OWASP API authorization guidance. Follow the current vendor contract when integrating the SDK; verdict fields and their recommended use may evolve.
I used AI assistance to prepare this reference. The example isn't a record of a shipped Play Integrity integration.
