Skip to content

Commit

Permalink
google-api-services-playintegrity is implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
musab.bozkurt committed Feb 4, 2024
1 parent df2d4f3 commit 003a960
Show file tree
Hide file tree
Showing 9 changed files with 268 additions and 16 deletions.
43 changes: 29 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<li><a href="#How_To_Run_And_Test_Dockerfile">How To Run And Test Application with Dockerfile (OPTIONAL)</a></li>
<li><a href="#How_To_Run_And_Test_Docker_Compose">How To Run And Test Application with docker-compose.yml (OPTIONAL)</a></li>
<li><a href="#Redis">Redis Commands</a></li>
<li><a href="#Google_Play_Integrity_API">Google Play Integrity API</a></li>
<li><a href="#References">References</a></li>
</ol>
</details>
Expand Down Expand Up @@ -121,29 +122,43 @@

### Redis

* The following command returns all matched data by `'keyPattern:*'` pattern
* `redis-cli --scan --pattern 'keyPattern:*'`
- The following command returns all matched data by `'keyPattern:*'` pattern
- `redis-cli --scan --pattern 'keyPattern:*'`

* The following command deletes all matched data by `'keyPattern:*'` pattern
* `redis-cli KEYS 'keyPattern:*' | xargs redis-cli DEL`
- The following command deletes all matched data by `'keyPattern:*'` pattern
- `redis-cli KEYS 'keyPattern:*' | xargs redis-cli DEL`

* The following command finds `TYPE` in redis with `KEY`
* `TYPE key` -> `TYPE xxx:hashedIdOrSomethingElse`
- The following command finds `TYPE` in redis with `KEY`
- `TYPE key` -> `TYPE xxx:hashedIdOrSomethingElse`

* The following commands search by `TYPE`
- The following commands search by `TYPE`
- for `"string" TYPE`: `get key`
- for `"hash" TYPE`: `hgetall key`
- for `"list" TYPE`: `lrange key 0 -1`
- for `"set" TYPE`: `smembers key`
- for `"zset" TYPE`: `zrange key 0 -1 withScores`

* for `"string" TYPE`: `get key`
* for `"hash" TYPE`: `hgetall key`
* for `"list" TYPE`: `lrange key 0 -1`
* for `"set" TYPE`: `smembers key`
* for `"zset" TYPE`: `zrange key 0 -1 withScores`
- `RedisInsight`: http://localhost:8001/

* RedisInsight:
-------

### Google_Play_Integrity_API

- [Setup Firebase](https://firebase.google.com/docs/admin/setup) or Just follow the following steps
to [Initialize the SDK, Create and Download Firebase Credentials JSON File](https://firebase.google.com/docs/admin/setup#initialize-sdk)
- After JSON file is downloaded, copy the content of downloaded file and replace `play-integrity-credentials.json`
content with that copied text
- Replace `application-name` value in application.yml file with `project_id` variable that is
in `play-integrity-credentials.json`
- [HELPFUL LINK BUT IT SHOULD NOT BE NEEDED](https://stackoverflow.com/a/40799378)

-------

### References

- [Metrics Made Easy Via Spring Actuator, Docker, Prometheus, and Grafana](https://www.youtube.com/watch?v=Utv7MWgNTvI)
- https://prometheus.io/docs/prometheus/latest/installation/#volumes-bind-mount
- [Spring Boot Rest Controller Unit Test with @WebMvcTest](https://www.bezkoder.com/spring-boot-webmvctest/)
- [Redis Commands](https://auth0.com/blog/introduction-to-redis-install-cli-commands-and-data-types/)
- [Running RedisInsight using Docker Compose](https://collabnix.com/running-redisinsight-using-docker-compose/)
- [Running RedisInsight using Docker Compose](https://collabnix.com/running-redisinsight-using-docker-compose/)
- [Google Play Integrity API](https://developer.android.com/google/play/integrity)
30 changes: 30 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
<dockerfile-maven-plugin.version>1.4.13</dockerfile-maven-plugin.version>
<registry>${env.CI_REGISTRY}/${env.CI_REGION}/${project.artifactId}</registry>
<spring-cloud-gcp-dependencies.version>1.2.8.RELEASE</spring-cloud-gcp-dependencies.version>
</properties>

<dependencies>
Expand Down Expand Up @@ -164,8 +165,37 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-playintegrity</artifactId>
<version>v1-rev20240101-2.0.0</version>
</dependency>

<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>2.3.0</version>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-starter-logging</artifactId>
</dependency>

</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-dependencies</artifactId>
<version>${spring-cloud-gcp-dependencies.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.mb.livedataservice.api.controller;

import com.mb.livedataservice.api.request.ApiPlayIntegrityTokenResult;
import com.mb.livedataservice.service.PlayIntegrityService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/validations/play-integrity")
public class PlayIntegrityController {

private final PlayIntegrityService playIntegrityService;

/**
* Create nonce for Play Integrity
**/
@GetMapping("/nonce")
@Operation(summary = "Create nonce for Play Integrity")
public Map<String, String> createNonce() {
log.info("Received a request to create nonce. createNonce");
return playIntegrityService.createNonce();
}

/**
* Verify Play Integrity token result.
*
* @param tokenResult to verify Play Integrity token result.
*/
@PostMapping(value = "/verify-token")
@Operation(summary = "Verify Play Integrity token result")
public Map<String, Object> verifyToken(@RequestBody ApiPlayIntegrityTokenResult tokenResult) {
log.info("Received a request to verify Play Integrity token result. verifyToken - ApiPlayIntegrityTokenResult:{}", tokenResult);
return playIntegrityService.verifyToken(tokenResult);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.mb.livedataservice.api.request;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

@Data
public class ApiPlayIntegrityTokenResult {

@Schema(description = "Play integrity token")
private String token;

// This is an optional field.
@Schema(description = "Package name which is mobile client package name", example = "com.mb.android")
private String packageName;

public String getPackageName() {
return this.packageName == null ? null : "com.mb.android";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.mb.livedataservice.config;

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.playintegrity.v1.PlayIntegrity;
import com.google.api.services.playintegrity.v1.PlayIntegrityRequestInitializer;
import com.google.api.services.playintegrity.v1.PlayIntegrityScopes;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;

@Slf4j
@Setter
@Configuration
@ConfigurationProperties("play-integrity-api")
public class PlayIntegrityConfiguration {

private String credentialsFile;
private String applicationName;

@Bean
public PlayIntegrity playIntegrity() throws GeneralSecurityException, IOException {
try {
ClassPathResource classPathResource = new ClassPathResource(credentialsFile);
if (classPathResource.exists()) {
return buildPlayIntegrityWithInputStream(classPathResource.getInputStream());
}
} catch (Exception e) {
log.error("Exception occurred while building PlayIntegrity bean. Exception: {}", ExceptionUtils.getStackTrace(e));
}
return buildPlayIntegrityWithCredentials(GoogleCredentials.newBuilder().build());
}

private PlayIntegrity buildPlayIntegrityWithInputStream(InputStream targetStream) throws IOException, GeneralSecurityException {
GoogleCredentials credentials = GoogleCredentials
.fromStream(targetStream)
.createScoped(PlayIntegrityScopes.PLAYINTEGRITY);
credentials.refreshIfExpired();

return buildPlayIntegrityWithCredentials(credentials);
}

private PlayIntegrity buildPlayIntegrityWithCredentials(GoogleCredentials googleCredentials) throws GeneralSecurityException, IOException {
return new PlayIntegrity.Builder(GoogleNetHttpTransport.newTrustedTransport(), new GsonFactory(), new HttpCredentialsAdapter(googleCredentials))
.setApplicationName(applicationName)
.setGoogleClientRequestInitializer(new PlayIntegrityRequestInitializer())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.mb.livedataservice.service;

import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.services.playintegrity.v1.PlayIntegrity;
import com.google.api.services.playintegrity.v1.model.DecodeIntegrityTokenRequest;
import com.google.api.services.playintegrity.v1.model.DecodeIntegrityTokenResponse;
import com.mb.livedataservice.api.request.ApiPlayIntegrityTokenResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Slf4j
@Component
@RequiredArgsConstructor
public class PlayIntegrityService {

private static final String RESULT = "result";
private static final String SUCCEEDED = "succeeded";

private final PlayIntegrity playIntegrity;

public Map<String, String> createNonce() {
String createdNonce = Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8));

HashMap<String, String> map = new HashMap<>();
map.put("createdNonce", createdNonce);
log.info("Nonce is created createNonce: {}", map);
return map;
}

public Map<String, Object> verifyToken(ApiPlayIntegrityTokenResult tokenResult) {
HashMap<String, Object> map = new HashMap<>();
try {
DecodeIntegrityTokenResponse response = playIntegrity.v1()
.decodeIntegrityToken(tokenResult.getPackageName(), new DecodeIntegrityTokenRequest().setIntegrityToken(tokenResult.getToken()))
.execute();

map.put(RESULT, response.getTokenPayloadExternal());
map.put(SUCCEEDED, true);
} catch (Exception e) {
log.error("Exception occurred while verifying play integrity token. Exception: {}", ExceptionUtils.getStackTrace(e));
if (e instanceof GoogleJsonResponseException) {
map.put(RESULT, ((GoogleJsonResponseException) e).getDetails());
} else {
map.put(RESULT, e.getMessage());
}
map.put(SUCCEEDED, false);
}
log.info("Play Integrity Token verification result: {}", map);
return map;
}
}
12 changes: 11 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ spring:
virtual:
enabled: true

cloud:
gcp:
credentials:
location: ${PLAY_INTEGRITY_API_CREDENTIALS_JSON:classpath:play-integrity-credentials.json}

management:
endpoints:
web:
Expand All @@ -97,4 +102,9 @@ redisson:

services:
token:
store: redis
store: redis

# https://developer.android.com/google/play/integrity/overview
play-integrity-api:
credentials-file: ${PLAY_INTEGRITY_API_CREDENTIALS_JSON:play-integrity-credentials.json}
application-name: project_id # project_id in play-integrity-credentials.json file
12 changes: 12 additions & 0 deletions src/main/resources/play-integrity-credentials.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"type": "type",
"project_id": "project_id",
"private_key_id": "private_key_id",
"private_key": "private_key",
"client_email": "client_email",
"client_id": "client_id",
"auth_uri": "auth_uri",
"token_uri": "token_uri",
"auth_provider_x509_cert_url": "auth_provider_x509_cert_url",
"client_x509_cert_url": "client_x509_cert_url"
}
7 changes: 6 additions & 1 deletion src/test/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,9 @@ redisson:

services:
token:
store: redis
store: redis

# https://developer.android.com/google/play/integrity/overview
play-integrity-api:
credentials-file: ${PLAY_INTEGRITY_API_CREDENTIALS_JSON:play-integrity-credentials-json-file}
application-name: project_id # project_id in play-integrity-credentials.json file

0 comments on commit 003a960

Please sign in to comment.