Skip to main content
ZombieMutex

30 results · ↑ ↓ navigate · Enter open · Esc close

All writing
November 6, 20263 min read

Android Play Integrity request binding and backend verdict policy

What requestHash protects, what the server must verify, and why an integrity verdict is not a substitute for authorization.

By

Share

Send this article to someone or copy its link.

Suggest a correction

Reader tools

3 min remaining · 0% of article

Select a passage and choose Highlight selection to save it. Local browser storage only. Shift+F toggles focus.

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:

  1. The request package name matches the expected application identity.
  2. The returned requestHash matches the backend-computed digest of the exact protected action.
  3. The token timestamp satisfies the service's freshness requirements.
  4. Relevant app and device verdicts meet the risk policy for the operation.
  5. The caller's independently authenticated session remains valid.
  6. 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.

typescript
type VerifiedRequest = {
  packageMatches: boolean
  hashMatches: boolean
  timestampFresh: boolean
  meetsIntegrityPolicy: boolean
  userCanPerformAction: boolean
}

function protectAction(checks: VerifiedRequest): boolean {
  return checks.packageMatches
    && checks.hashMatches
    && checks.timestampFresh
    && checks.meetsIntegrityPolicy
    && checks.userCanPerformAction
}

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 variationExpected policy behavior
Same verdict, different protected requestReject request hash mismatch
Valid verdict, wrong package identityReject
A stale token outside policyReject or explicit challenge refresh
Legitimate app, revoked accountReject
Integrity service unavailableRisk-specific degraded policy, never a silent success
Valid integrity, resource owned by another userReject 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.

Related technical notes with overlapping topics.

Discuss a passage

Select a paragraph above, copy it with its article link, then paste into the discussion. Don't include private data.

Jump to discussion ↓

Public conversation / GitHub Discussions

Discuss this article

Read and reply using GitHub Discussions. A GitHub account is needed to post. Comments are public and subject to GitHub moderation.

For privacy, the external giscus widget does not load until you choose to open it. Opening it connects your browser to giscus and GitHub. Do not post credentials or private project data.