Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

README.md

Play Integrity API: E2E Sample app

Note

Disclaimer: Non-Goals

This sample app is designed for demonstration and educational purposes, as a blueprint for technical integration. It does not provide a production-ready anti-abuse strategy. While the sample app demonstrates best practices for token handling and server-side verification, it is not a substitute for a comprehensive security audit.

Developers should treat the Play Integrity API as one signal within a broader, multi-layered anti-abuse strategy tailored to their specific business risks.

Warning

Security Notice: Cleartext HTTP in Development

Across the Android client, interactions rely on a dynamically configured base URL (BuildConfig.BASE_URL) that points to a local Node.js server using unencrypted HTTP (e.g., http://localhost:3000 or http://10.0.2.2:3000). This is an intentional design choice for this sample to minimize setup complexity and ensure frictionless onboarding, avoiding the need to generate and configure self-signed SSL/TLS certificates locally.

This configuration is strictly for local deployment. When integrating these concepts into your own production app, you should secure your network layer by using secure HTTPS (https://) and avoid having cleartext permissions in your network_security_config.xml.

Setup

To run the Sample App end-to-end, you must configure a Google Cloud project, register your app in the Google Play Console, and set up both the local Node.js server and the Android client.

Prerequisites

  • Node.js v18 or higher installed
  • The latest version of Android Studio installed
  • A Google Play Developer account
  • A Google Cloud account

Step 1: Configure Google Cloud & Play Console

First, establish the connection between your Google Cloud project and your Google Play app entry.

  1. Open the Google Cloud Console and create a new project.
  2. Navigate to APIs & Services > Library, search for the Google Play Integrity API, and click Enable.
  3. Open the Google Play Console and create a new app entry.
    • Note: Choose your package name carefully. You will use this exact package name to configure both the Android client and the Node.js server later.
  4. In the Play Console left navigation menu, select Protected with Play.
  5. Click Get Started on the Play Integrity API card.
  6. Follow the on-screen instructions to link the Google Cloud project you created in step 1.
  7. Enable the population of the following optional verdicts within the same section:
    • MEETS_STRONG_INTEGRITY
    • MEETS_BASIC_INTEGRITY
    • Device attributes
    • App access risk
    • Play Protect

Step 2: Generate Service Account Credentials

Your local server needs credentials to securely communicate with Google Cloud.

  1. In the Google Cloud Console, navigate to IAM & Admin > Service Accounts.
  2. Click Create Service Account. (Default settings are fine; no special roles are required).
  3. Select your newly created service account, select the Keys tab, and click Add Key > Create new key.
  4. Select JSON as the key type and click Create to download the credentials file to your machine.

Step 3: Download the Project

Clone the repository containing the sample code to your local machine.

git clone https://github.com/android/security-samples.git
cd security-samples

Step 4: Set up Local Server

Configure and run the Node.js backend.

  1. Navigate to the server directory: cd PlayIntegrityAPI/node-server
  2. Install the required dependencies: npm install
  3. Move the downloaded JSON credentials file from Step 2 into the root of the node-server directory.
  4. Rename the file to google-credentials.json
    • Note: This filename is listed in .gitignore to prevent accidental credential leaks
  5. Create a file named .env in the root of the node-server directory and define the following variables:
    • PACKAGE_NAME="com.your.package.name" # Use the package name of the Play Console app entry created in Step 1
    • GOOGLE_CREDENTIALS_PATH="./google-credentials.json"
  6. Start the server: node app.js

Step 5: Set up Android Client

Configure the Android app to communicate with your local server and your specific Google Cloud project.

  1. Open the PlayIntegrityAPI/android-client directory using Android Studio
  2. Open the local.properties file in the project root and add your Google Cloud project number:
    • GCP_PROJECT_NUMBER=1234567890
  3. Open the app-level build.gradle.kts file and update the applicationId to match the package name of the app entry you created in the Play Console.
  4. Sync your project with Gradle files.
  5. Select the physicalRelease variant from the Build Variants tool window, opened via the Tool Window bar on the far left of the Android Studio interface
  6. Generate a signed Android App Bundle (AAB) using Android Studio.
  7. In the Google Play Console, set up an internal testing track and upload your signed AAB as a new release.
  8. Once the release is processed, use the internal testing link provided in the Play Console to install the app onto your physical test device.
  9. To allow the app on your device to communicate with your local machine's backend server, connect the device via USB and set up ADB reverse port forwarding in your terminal (replace <SERVER_PORT> with your Node server port, e.g., 3000, and <CLIENT_PORT> with the port number that the app tries to access, e.g. 3000): adb reverse tcp:<CLIENT_PORT> tcp:<SERVER_PORT>
    • Note: once the device is disconnected, you will need to run this command again the next time you need to test this flow

Banking Micro-App

The Banking micro-app demonstrates how to securely parse HTTP requests, cryptographically validate Play Integrity tokens, and enforce business rules.

Banking App Flow

User Journey Overview

When a user attempts to submit a secure transfer, the app requests an integrity token. If the device fails the integrity checks (e.g., a compromised device or unlicensed app), a remediation dialog prompts the user to resolve the issue (such as installing from Google Play). Upon successful remediation, the app retries the transaction securely.

Client-Side Implementation

See the following files in android-client/feature/bank:

Server-Side Implementation

See the following files in node-server/src/features/bank:


Streaming Micro-App

The Streaming micro-app demonstrates how to parse standard integrity tokens, enforce tiered access policies, and dynamically modify DASH XML manifests.

Streaming App Flow

User Journey Overview

The user accesses video content, which requests an integrity token to determine their device's trust tier. Based on the returned token, the backend dynamically modifies the video manifest to serve either premium or restricted streams. A user on a verified device enjoys high-quality streaming, while an unrecognized environment receives degraded quality without outright blocking playback.

Client-Side Implementation

See the following files in android-client/feature/streaming:

Server-Side Implementation

See the following files in node-server/src/features/streaming:


Game Micro-App

The Game micro-app showcases a stateful, secure verification pattern designed to defeat TOCTOU (Time-of-Check to Time-of-Use) cheats, enforce strict environment policies, and securely evaluate background Play Integrity API attestations.

Game App Flow

User Journey Overview

Upon initiating a game session, a secure state is established on the server. While playing, the app performs background checks and sends periodic updates. If an anomaly is detected (like an attached debugger or a compromised environment), the game pauses and prompts the user for remediation. Once the environment is secure again, gameplay resumes, culminating in a securely validated final score submission.

Client-Side Implementation

See the following files in android-client/feature/game:

Server-Side Implementation

See the following files in node-server/src/features/game:


Test Play Console Integrity Responses

This section guides you through using the Play Integrity API test responses feature in the Google Play Console to dynamically alter the streaming quality in the Streaming micro-app. This assumes you have already completed the full end-to-end setup as described in the root project guide (i.e. app created in Play Console, Play Integrity API enabled, Google Cloud project linked, Node.js server running, Android app buildable).

Prerequisites

  • A Google Play Developer account.
  • Your app is set up in the Play Console.
  • Play Integrity API is enabled for your app and linked to your Google Cloud project.
  • The sample Node.js backend server is running.
  • The Android client app is installed and runnable on a device or emulator, signed in with a Google account.

Steps to Test Different Integrity Verdicts

  1. Navigate to Play Integrity API Settings:
    • Open the Google Play Console.
    • Select your application.
    • In the Play Console left navigation menu, select Protected with Play.
    • On the Protected with Play page, locate the Play Integrity API row and click Manage.

Navigating to Play Integrity API settings on Play Console

  1. Configure Test Responses:
    • Scroll down to the Testing section.
    • Click Create new test.
    • Give your test a descriptive name (e.g., "Device Unrecognized Test").
    • Under Email lists, select or create an email list containing the Google account(s) used on your test device(s).
    • Modify the Integrity verdicts to simulate different scenarios. For example:
      • Premium Quality (Fully Trusted):
        • appRecognitionVerdict: PLAY_RECOGNIZED
        • deviceRecognitionVerdict: [MEETS_DEVICE_INTEGRITY, MEETS_STRONG_INTEGRITY]
        • appLicensingVerdict: LICENSED
      • Basic Quality (Basic Integrity):
        • appRecognitionVerdict: PLAY_RECOGNIZED
        • deviceRecognitionVerdict: [MEETS_BASIC_INTEGRITY]
        • appLicensingVerdict: LICENSED
      • Restricted Quality (No Device Integrity):
        • appRecognitionVerdict: UNEVALUATED
        • deviceRecognitionVerdict: [] (Empty)
        • appLicensingVerdict: UNEVALUATED

UI to configure a test response

  1. Save the Test Configurations:
    • Click Create test. You might need to click Save changes at the bottom of the page too. Propagation time varies depending on multiple factors, but changes should be reflected in about 1-2 hours at the longest.
  2. Observe in the Android App:
    • Open the sample app on your test device and navigate to the Streaming micro-app (ensuring the device is signed in with one of the accounts from the email list in the test configuration).
    • The app might show a quality level based on a previous integrity check.
    • Click the Refresh Integrity Check button within the app. This action forces the app to request a new Play Integrity token and DASH manifest.
    • Play Integrity API will return a token with the verdicts you configured in the Play Console test.
    • The Node.js server will decode this test token and return a DASH manifest filtered according to the tier mapped to the received verdicts.
    • Observe the UI: The highlighted tier card ("Premium", "Standard", or "Restricted") should update, and the video playback quality will adjust after ExoPlayer reloads the manifest.

Example Scenarios to try:

  • Simulate a Rooted/Compromised Device: Set deviceRecognitionVerdict to be empty. The stream should degrade to the "Restricted" tier.
  • Simulate an Unlicensed User: Set appLicensingVerdict to UNLICENSED. The stream should also degrade to the "Restricted" tier.
  • Simulate a Fully Trusted Device & Licensed User: Ensure verdicts are MEETS_STRONG_INTEGRITY, PLAY_RECOGNIZED, and LICENSED. The stream should allow "Premium" quality.

By changing the test responses in the Play Console and using the "Refresh Integrity Check" button, you can effectively test how the end-to-end integration handles various Play Integrity API outcomes and confirm that the stream quality adjusts dynamically as expected.


Support

If you encounter issues or have feedback specific to this sample app, please file an issue in this repository.