// Copyright 2022 Luca Casonato. All rights reserved. MIT license. /** * Google Play Integrity API Client for Deno * ========================================= * * The Play Integrity API helps you check that you're interacting with your genuine app on a genuine Android device powered by Google Play services. The Play Integrity API has replaced SafetyNet Attestation and Android Device Verification. * * Docs: https://developer.android.com/google/play/integrity * Source: https://googleapis.deno.dev/v1/playintegrity:v1.ts */ import { auth, CredentialsClient, GoogleAuth, request } from "/_/base@v1/mod.ts"; export { auth, GoogleAuth }; export type { CredentialsClient }; /** * The Play Integrity API helps you check that you're interacting with your * genuine app on a genuine Android device powered by Google Play services. The * Play Integrity API has replaced SafetyNet Attestation and Android Device * Verification. */ export class PlayIntegrity { #client: CredentialsClient | undefined; #baseUrl: string; constructor(client?: CredentialsClient, baseUrl: string = "https://playintegrity.googleapis.com/") { this.#client = client; this.#baseUrl = baseUrl; } /** * Writes recall bits for the device where Play Integrity API token is * obtained. The endpoint is available to select Play partners in an early * access program (EAP). * * @param packageName Required. Package name of the app the attached integrity token belongs to. */ async deviceRecallWrite(packageName: string, req: WriteDeviceRecallRequest): Promise { const url = new URL(`${this.#baseUrl}v1/${ packageName }/deviceRecall:write`); const body = JSON.stringify(req); const data = await request(url.href, { client: this.#client, method: "POST", body, }); return data as WriteDeviceRecallResponse; } /** * Decodes the integrity token and returns the token payload. * * @param packageName Package name of the app the attached integrity token belongs to. */ async v1DecodeIntegrityToken(packageName: string, req: DecodeIntegrityTokenRequest): Promise { const url = new URL(`${this.#baseUrl}v1/${ packageName }:decodeIntegrityToken`); const body = JSON.stringify(req); const data = await request(url.href, { client: this.#client, method: "POST", body, }); return deserializeDecodeIntegrityTokenResponse(data); } } /** * (Restricted Access) Contains a signal helping apps differentiating between * likely genuine and likely non-genuine user traffic. */ export interface AccountActivity { /** * Required. Indicates the activity level of the account. */ activityLevel?: | "ACTIVITY_LEVEL_UNSPECIFIED" | "UNEVALUATED" | "UNUSUAL" | "UNKNOWN" | "TYPICAL_BASIC" | "TYPICAL_STRONG"; } /** * Contains the account information such as the licensing status for the user * in the scope. */ export interface AccountDetails { /** * (Restricted Access) Details about the account activity for the user in the * scope. */ accountActivity?: AccountActivity; /** * Required. Details about the licensing status of the user for the app in * the scope. */ appLicensingVerdict?: | "UNKNOWN" | "LICENSED" | "UNLICENSED" | "UNEVALUATED"; } /** * Contains signals about others apps on the device which could be used to * access or control the requesting app. */ export interface AppAccessRiskVerdict { /** * List of detected app types signalled for App Access Risk. */ appsDetected?: | "APPS_DETECTED_UNSPECIFIED" | "KNOWN_INSTALLED" | "KNOWN_CAPTURING" | "KNOWN_OVERLAYS" | "KNOWN_CONTROLLING" | "UNKNOWN_INSTALLED" | "UNKNOWN_CAPTURING" | "UNKNOWN_OVERLAYS" | "UNKNOWN_CONTROLLING"[]; /** * Deprecated: this field will be removed, please use apps_detected instead. * App access risk verdict related to apps that are not installed by Google * Play, and are not preloaded on the system image by the device manufacturer. */ otherApps?: | "UNKNOWN" | "UNEVALUATED" | "NOT_INSTALLED" | "INSTALLED" | "CAPTURING" | "CONTROLLING"; /** * Deprecated: this field will be removed, please use apps_detected instead. * App access risk verdict related to apps that are not installed by the * Google Play Store, and are not preloaded on the system image by the device * manufacturer. */ playOrSystemApps?: | "UNKNOWN" | "UNEVALUATED" | "NOT_INSTALLED" | "INSTALLED" | "CAPTURING" | "CONTROLLING"; } /** * Contains the application integrity information. */ export interface AppIntegrity { /** * Required. Details about the app recognition verdict */ appRecognitionVerdict?: | "UNKNOWN" | "PLAY_RECOGNIZED" | "UNRECOGNIZED_VERSION" | "UNEVALUATED"; /** * The SHA256 hash of the requesting app's signing certificates (base64 * web-safe encoded). Set iff app_recognition_verdict != UNEVALUATED. */ certificateSha256Digest?: string[]; /** * Package name of the application under attestation. Set iff * app_recognition_verdict != UNEVALUATED. */ packageName?: string; /** * Version code of the application. Set iff app_recognition_verdict != * UNEVALUATED. */ versionCode?: bigint; } function serializeAppIntegrity(data: any): AppIntegrity { return { ...data, versionCode: data["versionCode"] !== undefined ? String(data["versionCode"]) : undefined, }; } function deserializeAppIntegrity(data: any): AppIntegrity { return { ...data, versionCode: data["versionCode"] !== undefined ? BigInt(data["versionCode"]) : undefined, }; } /** * Request to decode the integrity token. */ export interface DecodeIntegrityTokenRequest { /** * Encoded integrity token. */ integrityToken?: string; } /** * Response containing the decoded integrity payload. */ export interface DecodeIntegrityTokenResponse { /** * Plain token payload generated from the decoded integrity token. */ tokenPayloadExternal?: TokenPayloadExternal; } function serializeDecodeIntegrityTokenResponse(data: any): DecodeIntegrityTokenResponse { return { ...data, tokenPayloadExternal: data["tokenPayloadExternal"] !== undefined ? serializeTokenPayloadExternal(data["tokenPayloadExternal"]) : undefined, }; } function deserializeDecodeIntegrityTokenResponse(data: any): DecodeIntegrityTokenResponse { return { ...data, tokenPayloadExternal: data["tokenPayloadExternal"] !== undefined ? deserializeTokenPayloadExternal(data["tokenPayloadExternal"]) : undefined, }; } /** * Contains information about the device for which the integrity token was * generated, e.g. Android SDK version. */ export interface DeviceAttributes { /** * Android SDK version of the device, as defined in the public Android * documentation: * https://developer.android.com/reference/android/os/Build.VERSION_CODES. It * won't be set if a necessary requirement was missed. For example * DeviceIntegrity did not meet the minimum bar. */ sdkVersion?: number; } /** * Contains the device attestation information. */ export interface DeviceIntegrity { /** * Attributes of the device where the integrity token was generated. */ deviceAttributes?: DeviceAttributes; /** * Details about the device recall bits set by the developer. */ deviceRecall?: DeviceRecall; /** * Details about the integrity of the device the app is running on. */ deviceRecognitionVerdict?: | "UNKNOWN" | "MEETS_BASIC_INTEGRITY" | "MEETS_DEVICE_INTEGRITY" | "MEETS_STRONG_INTEGRITY" | "MEETS_VIRTUAL_INTEGRITY"[]; /** * Contains legacy details about the integrity of the device the app is * running on. Only for devices with Android version T or higher and only for * apps opted in to the new verdicts. Only available during the transition * period to the new verdicts system and will be removed afterwards. */ legacyDeviceRecognitionVerdict?: | "UNKNOWN" | "MEETS_BASIC_INTEGRITY" | "MEETS_DEVICE_INTEGRITY" | "MEETS_STRONG_INTEGRITY" | "MEETS_VIRTUAL_INTEGRITY"[]; /** * Details about the device activity of the device the app is running on. */ recentDeviceActivity?: RecentDeviceActivity; } /** * Contains the recall bits per device set by the developer. */ export interface DeviceRecall { /** * Required. Contains the recall bits values. */ values?: Values; /** * Required. Contains the recall bits write dates. */ writeDates?: WriteDates; } /** * Contains information about the environment Play Integrity API runs in, e.g. * Play Protect verdict. */ export interface EnvironmentDetails { /** * The evaluation of the App Access Risk verdicts. */ appAccessRiskVerdict?: AppAccessRiskVerdict; /** * The evaluation of Play Protect verdict. */ playProtectVerdict?: | "PLAY_PROTECT_VERDICT_UNSPECIFIED" | "UNEVALUATED" | "NO_ISSUES" | "NO_DATA" | "MEDIUM_RISK" | "HIGH_RISK" | "POSSIBLE_RISK"; } /** * Recent device activity can help developers identify devices that have * exhibited hyperactive attestation activity, which could be a sign of an * attack or token farming. */ export interface RecentDeviceActivity { /** * Required. Indicates the activity level of the device. */ deviceActivityLevel?: | "DEVICE_ACTIVITY_LEVEL_UNSPECIFIED" | "UNEVALUATED" | "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4"; } /** * Contains the integrity request information. */ export interface RequestDetails { /** * Nonce that was provided in the request (which is base64 web-safe no-wrap). */ nonce?: string; /** * Request hash that was provided in the request. */ requestHash?: string; /** * Required. Application package name this attestation was requested for. * Note: This field makes no guarantees or promises on the caller integrity. * For details on application integrity, check application_integrity. */ requestPackageName?: string; /** * Required. Timestamp, in milliseconds, of the integrity application * request. */ timestampMillis?: bigint; } function serializeRequestDetails(data: any): RequestDetails { return { ...data, timestampMillis: data["timestampMillis"] !== undefined ? String(data["timestampMillis"]) : undefined, }; } function deserializeRequestDetails(data: any): RequestDetails { return { ...data, timestampMillis: data["timestampMillis"] !== undefined ? BigInt(data["timestampMillis"]) : undefined, }; } /** * Contains additional information generated for testing responses. */ export interface TestingDetails { /** * Required. Indicates that the information contained in this payload is a * testing response that is statically overridden for a tester. */ isTestingResponse?: boolean; } /** * Contains basic app information and integrity signals like device attestation * and licensing details. */ export interface TokenPayloadExternal { /** * Required. Details about the Play Store account. */ accountDetails?: AccountDetails; /** * Required. Details about the application integrity. */ appIntegrity?: AppIntegrity; /** * Required. Details about the device integrity. */ deviceIntegrity?: DeviceIntegrity; /** * Details of the environment Play Integrity API runs in. */ environmentDetails?: EnvironmentDetails; /** * Required. Details about the integrity request. */ requestDetails?: RequestDetails; /** * Indicates that this payload is generated for testing purposes and contains * any additional data that is linked with testing status. */ testingDetails?: TestingDetails; } function serializeTokenPayloadExternal(data: any): TokenPayloadExternal { return { ...data, appIntegrity: data["appIntegrity"] !== undefined ? serializeAppIntegrity(data["appIntegrity"]) : undefined, requestDetails: data["requestDetails"] !== undefined ? serializeRequestDetails(data["requestDetails"]) : undefined, }; } function deserializeTokenPayloadExternal(data: any): TokenPayloadExternal { return { ...data, appIntegrity: data["appIntegrity"] !== undefined ? deserializeAppIntegrity(data["appIntegrity"]) : undefined, requestDetails: data["requestDetails"] !== undefined ? deserializeRequestDetails(data["requestDetails"]) : undefined, }; } /** * Contains the recall bits values. */ export interface Values { /** * Required. First recall bit value. */ bitFirst?: boolean; /** * Required. Second recall bit value. */ bitSecond?: boolean; /** * Required. Third recall bit value. */ bitThird?: boolean; } /** * Contains the recall bits write dates. */ export interface WriteDates { /** * Optional. Write time in YYYYMM format (in UTC, e.g. 202402) for the first * bit. Note that this value won't be set if the first bit is false. */ yyyymmFirst?: number; /** * Optional. Write time in YYYYMM format (in UTC, e.g. 202402) for the second * bit. Note that this value won't be set if the second bit is false. */ yyyymmSecond?: number; /** * Optional. Write time in YYYYMM format (in UTC, e.g. 202402) for the third * bit. Note that this value won't be set if the third bit is false. */ yyyymmThird?: number; } /** * Request to write device recall bits. */ export interface WriteDeviceRecallRequest { /** * Required. Integrity token obtained from calling Play Integrity API. */ integrityToken?: string; /** * Required. The new values for the device recall bits to be written. */ newValues?: Values; } /** * Response for the Write Device Recall action. Currently empty. */ export interface WriteDeviceRecallResponse { }