'Decode integrity token using Google PlayIntegrity API
I am trying to implement PlayIntegrity API to my Android app, but I don't know how to decrypt and verify the token using Google's servers.
I followed the documentation up to this point:
And now I am stuck on making the decode request to googleapis
. I don't understand how does this instruction work.
I created a Service Account and I downloaded JSON credentials file and put it into my Laravel project, then I tried this piece of code:
$client = new Client();
$client->setAuthConfig(storage_path('app/integrity_check_account.json'));
$client->addScope(PlayIntegrity::class);
$httpClient = $client->authorize();
$result = $httpClient->request('POST', 'https://playintegrity.googleapis.com/v1/my.package.name', [
'headers' => ['Content-Type' => 'application/json'],
'body' => "{ 'integrity_token': 'token' }"
]);
dd($result);
So I having two issues with this code:
- Am I adding the scope correctly?
- Am I making the request correctly? Because it is not working as I am getting 404 error.
Solution 1:[1]
I finally found the solution to my problem while looking at the source of the PlayIntegrity API
from the Google APIs Client Library for PHP.
After importing required dependencies:
composer require google/apiclient:^2.12.1
This is my controller:
<?php
namespace App\Http\Controllers;
use Google\Client;
use Google\Service\PlayIntegrity;
use Google\Service\PlayIntegrity\DecodeIntegrityTokenRequest;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController {
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
public function performCheck(Request $request) {
$client = new Client();
$client->setAuthConfig(path/to/your/credentials/json/file.json);
$client->addScope(PlayIntegrity::PLAYINTEGRITY);
$service = new PlayIntegrity($client);
$tokenRequest = new DecodeIntegrityTokenRequest();
$tokenRequest->setIntegrityToken("TOKEN_HERE");
$result = $service->v1->decodeIntegrityToken('PACKGE_NAME_HERE', $tokenRequest);
//check result logic here
}
}
Solution 2:[2]
Thanks for this post, I found it very helpful.
Even so, I still had some problems decoding the verdict. Here are two problems I ran into, and I wasn't sure whether the problem was with how I was calling the Play Integrity API on the device or how I was decoding the response token.
When you decode the verdict token, if you get an exception with error code 400/Request contains an invalid argument, you probably need to set the Cloud project number with IntegrityTokenRequest_setCloudProjectNumber()
(C++).
If you get an exception decoding the token with error code 403/The caller does not have permission, double-check that you've set the correct Google Cloud Project number.
Solution 3:[3]
You have to get access token before calling the Play Integrity API. See below 2 request:
POST /token HTTP/1.1
Accept-Encoding: gzip, deflate
User-Agent: Google-HTTP-Java-Client/1.41.1 (gzip)
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Host: oauth2.googleapis.com
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: close
Content-Length: 811
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsImtpZCI6IjVhY2Y5NjJkNDExZmZiZDE1NmIxZTE3ODcwY2Y0ZGExYjU0ZmM4MGIiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL29hdXRoMi5nb29nbGVhcGlzLmNvbS90b2tlbiIsImV4cCI6MTY0ODc3NjU2OCwiaWF0IjoxNjQ4NzcyOTY4LCJpc3MiOiJwbGF5LWludGVncml0eS1mZG5iLXRlc3RAZmRuYi1wbGF5LWludGVncml0eS10ZXN0LmlhbS5nc2VydmljZWFjY291bnQuY29tIiwic2NvcGUiOiJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9hdXRoL3BsYXlpbnRlZ3JpdHkifQ.TQM6UFswVl1oe2JLDiPIjgoEyX89eefegh1EiAd3u8ZvO3STbp7g5rgUBC03_3jH0mLspZ4nbGH7m_8cKaYdKbyVs--P7Um591QU68FJxEvG0Nxr-8mjejo-mL4Z5bxXGVTVnd9n2hkWaBEe7iQ7dcqdkRHXNS1Tg2CcLWbCU1q0pxfAtAEe1mRXj5Y-VYfVl-PiN8Cl4Q8ZEbEAPyBkP-eqSMQcMA0nwhgsmIR4JxRH3zbef20SBuZgm0GBPsngUaseyvni-yjGcTmcyB5Sa1CSQL6-384016G9X7jIytF3fOY1pjl0L-N6KD6JmB4fC6ApDYqQmyZhfb5BD4nsjA
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Vary: Origin
Vary: X-Origin
Vary: Referer
Date: Fri, 01 Apr 2022 00:29:30 GMT
Server: scaffolding on HTTPServer2
Cache-Control: private
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
Connection: close
Content-Length: 1083
{"access_token":"ya29.c.b0AXv0zTNFkyzpv-uCAecXsZ8U1TelBGDjRVqBckImapqKoYukyNziQ_zsKecAIns4qjS6UeSiY9bSI3cysPbg7jjeBw63079wuKtsX25yDj83WSK2yzUPKev5MfoyJCyRmRmv-SMHYbqq2qQnn5SZiWM6lNV7hisch_s9JcSe3HmRS-ko9R670ywpgMIvzhADl5tSJlD0xwQyulrNRcJDNkNwzum0e-8........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................","expires_in":3599,"token_type":"Bearer"}
POST /v1/com.example.playinegrity:decodeIntegrityToken HTTP/1.1
Accept-Encoding: gzip, deflate
Authorization: Bearer ya29.c.b0AXv0zTNFkyzpv-uCAecXsZ8U1TelBGDjRVqBckImapqKoYukyNziQ_zsKecAIns4qjS6UeSiY9bSI3cysPbg7jjeBw63079wuKtsX25yDj83WSK2yzUPKev5MfoyJCyRmRmv-SMHYbqq2qQnn5SZiWM6lNV7hisch_s9JcSe3HmRS-ko9R670ywpgMIvzhADl5tSJlD0xwQyulrNRcJDNkNwzum0e-8........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
User-Agent: Google-API-Java-Client/1.33.1 Google-HTTP-Java-Client/1.41.1 (gzip)
x-goog-api-client: gl-java/1.8.0 gdcl/1.33.1 mac-os-x/11.6.2
Content-Type: application/json; charset=UTF-8
Content-Encoding: gzip
Host: playintegrity.googleapis.com
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: close
Content-Length: 712
[GZIP Content]
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Vary: Origin
Vary: X-Origin
Vary: Referer
Date: Fri, 01 Apr 2022 00:29:33 GMT
Server: ESF
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
Cache-Control: private, proxy-revalidate
Connection: close
Content-Length: 649
{
"tokenPayloadExternal": {
"requestDetails": {
"requestPackageName": "com.example.playinegrity",
"timestampMillis": "1648699890779",
"nonce": "YWJjZGVmZ2hpajEyMzQ1Njc4OTE="
},
"appIntegrity": {
"appRecognitionVerdict": "UNRECOGNIZED_VERSION",
"packageName": "com.example.playinegrity",
"certificateSha256Digest": [
"JAHNMZrOYvOOVQ40zNWm2e4fTmHIFYGo-_rvgk7vs4o"
],
"versionCode": "1"
},
"deviceIntegrity": {
"deviceRecognitionVerdict": [
"MEETS_DEVICE_INTEGRITY"
]
},
"accountDetails": {
"appLicensingVerdict": "UNEVALUATED"
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | hiddeneyes02 |
Solution 2 | James Fairweather |
Solution 3 | Will Luo |