Skip to content

Conversation

@wickedcube
Copy link
Contributor

Adds a sample implementation demonstrating how to migrate user authentication from PGS v1 to v2 while preserving identity via the OpenID sub claim.

Includes:

  • Server (server.js): Unifies legacy v1 ID Token verification and new v2 Auth Code exchange into a single user identity.
  • Client (AuthManager.cs): Uses preprocessor symbols (PGS_V1, PGS_V2) to toggle between the different SDK implementations.
  • Docs (README.md): Full guide on dependency cleanup, Client ID configuration, and testing the upgrade path.

@wickedcube wickedcube self-assigned this Dec 8, 2025
@gemini-code-assist
Copy link

Summary of Changes

Hello @wickedcube, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers a crucial sample implementation designed to guide developers through the process of migrating user authentication from Google Play Games Services v1 to v2. The core innovation lies in its ability to maintain a player's identity across this transition by consistently utilizing the OpenID sub claim, preventing data loss or account fragmentation. The solution comprises a Node.js backend that intelligently processes both legacy and modern authentication tokens, and a Unity client that can be configured to simulate either v1 or v2 environments, complete with comprehensive setup and testing instructions.

Highlights

  • PGS v1 to v2 Migration Sample: Introduces a sample demonstrating how to migrate user authentication from Google Play Games Services (PGS) v1 to v2 while preserving user identity.
  • Unified Backend Authentication: The server.js backend now handles both PGS v1 ID Token verification and PGS v2 Auth Code exchange, unifying them based on the OpenID sub claim to ensure consistent user identity.
  • Client-Side Version Toggling: The AuthManager.cs Unity client uses preprocessor symbols (PGS_V1, PGS_V2) to easily switch between the v1 and v2 SDK implementations, facilitating testing and development.
  • Comprehensive Documentation: A detailed README.md is included, providing a full guide on dependency cleanup, client ID configuration, and a step-by-step process for testing the upgrade path.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a valuable sample for migrating from Google Play Games Services v1 to v2, complete with a Node.js backend and a Unity client. The implementation is well-documented and effectively demonstrates the core migration path using the OpenID sub claim for identity preservation.

My review identifies several critical and high-severity issues that should be addressed. These include a security vulnerability in the Unity project settings that allows insecure HTTP requests in production, a critical bug in the Node.js server that will cause it to crash on supported Node versions, and potential memory leaks in the C# client from undisposed UnityWebRequest objects. I've also provided suggestions to improve code maintainability, configuration, and robustness.

Comment on lines 209 to 210
const debugResponse = await fetch(debugUrl);
const debugData = await debugResponse.json();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The README.md specifies Node.js v14+, but the native fetch API was only stabilized in v18. Using fetch here will cause the server to crash on Node.js versions 14, 16, and 17.

Since axios is already a dependency, you should use it for HTTP requests to ensure compatibility.

  1. Add const axios = require('axios'); at the top of the file.
  2. Replace this fetch call and the one on line 222 with axios.
Suggested change
const debugResponse = await fetch(debugUrl);
const debugData = await debugResponse.json();
const debugResponse = await axios.get(debugUrl);
const debugData = debugResponse.data;

platformRequiresReadableAssets: 0
virtualTexturingSupportEnabled: 0
insecureHttpOption: 0
insecureHttpOption: 2

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Setting insecureHttpOption to 2 (AlwaysAllowed) allows the application to make insecure HTTP requests in all builds, including production releases. This is a significant security risk. As noted in the README.md, this should be set to Allowed in development builds, which corresponds to a value of 1.

  insecureHttpOption: 1

Comment on lines +14 to +18
"axios": "^1.13.1",
"dotenv": "^17.2.3",
"express": "^5.1.0",
"google-auth-library": "^10.4.1",
"jsonwebtoken": "^9.0.2"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Several of the package versions listed in dependencies do not correspond to public releases on npm (e.g., axios, dotenv, express, google-auth-library). This will likely cause npm install to fail unless you are using a private registry. Please verify these versions and update them to valid public versions if applicable.

Example with current public versions:

"dependencies": {
  "axios": "^1.7.2",
  "dotenv": "^16.4.5",
  "express": "^4.19.2",
  "google-auth-library": "^9.11.0",
  "jsonwebtoken": "^9.0.2"
}
Suggested change
"axios": "^1.13.1",
"dotenv": "^17.2.3",
"express": "^5.1.0",
"google-auth-library": "^10.4.1",
"jsonwebtoken": "^9.0.2"
"axios": "^1.7.2",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"google-auth-library": "^9.11.0",
"jsonwebtoken": "^9.0.2"

statusText.text = "Initializing Google Sign-In...";
GoogleSignIn.Configuration = new GoogleSignInConfiguration
{
WebClientId = "",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The WebClientId is hardcoded as an empty string, which will cause the PGS v2 authentication flow to fail. For easier configuration in a sample project, you should expose this as a public field in the Unity Inspector instead of requiring developers to edit the script directly.

First, add a public field to the class (e.g., near serverUrl):

public string serverUrl;
public string webClientId;

Then, use this field in the configuration.

            WebClientId = webClientId,

string jsonPayload = JsonUtility.ToJson(requestData);
byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonPayload);

UnityWebRequest request = new UnityWebRequest(verify_and_link_google, "POST");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

UnityWebRequest implements IDisposable. Failing to dispose of it can lead to memory leaks and performance issues. You should wrap UnityWebRequest instances in a using block to ensure they are disposed of correctly, even if an error occurs. This applies to all UnityWebRequest usages in this file (in ExchangeAuthcodeAndLink, PostScore, and VerifyAndLinkFacebookAccount).

Example:

using (UnityWebRequest request = new UnityWebRequest(verify_and_link_google, "POST"))
{
    // ... setup and send request
    yield return request.SendWebRequest();
    // ... handle response
}

Comment on lines +5 to +8
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The main entry points to index.js, but the server file is server.js. This should be corrected to avoid issues with tools that rely on this field. Additionally, adding a start script is a common convention that makes running the server easier (npm start).

Suggested change
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"main": "server.js",
"scripts": {
"start": "node server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},

1. **Install Dependencies**:

```bash
npm install express google-auth-library jsonwebtoken dotenv

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The npm install command is missing the axios package. This package is a dependency in package.json and is required for the Facebook authentication flow to work correctly across all supported Node.js versions (once fetch is replaced).

Suggested change
npm install express google-auth-library jsonwebtoken dotenv
npm install express google-auth-library jsonwebtoken dotenv axios

Comment on lines +85 to +99
let inGameAccountID;

if (userDatabase.has(dbKey)) {
// User already exists, retrieve their ID
inGameAccountID = userDatabase.get(dbKey);
console.log(`(PGS v1) Existing user. In-Game ID: ${inGameAccountID}`);
} else {
// New user, create a new in-game ID and store it
inGameAccountID = `ingame-${nextInGameAccountId++}`;

userDatabase.set(dbKey, inGameAccountID);
inGameDatabase.set(inGameAccountID, 0);

console.log(`(PGS v1) New user. Created In-Game ID: ${inGameAccountID}`);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for finding or creating a user is duplicated across the /verify_and_link_google, /exchange_authcode_and_link, and /verify_and_link_facebook endpoints. This makes the code harder to maintain.

Consider refactoring this logic into a single helper function to reduce code duplication and make future changes easier.

Example:

function findOrCreateUser(dbKey, provider) {
    let inGameAccountID;
    if (userDatabase.has(dbKey)) {
        inGameAccountID = userDatabase.get(dbKey);
        console.log(`(${provider}) Existing user. In-Game ID: ${inGameAccountID}`);
    } else {
        inGameAccountID = `ingame-${nextInGameAccountId++}`;
        userDatabase.set(dbKey, inGameAccountID);
        inGameDatabase.set(inGameAccountID, 0);
        console.log(`(${provider}) New user. Created In-Game ID: ${inGameAccountID}`);
    }
    return inGameAccountID;
}

{
#if PGS_V1
public string idToken;
public string playerID;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The playerID field in the GoogleAuthRequest class is defined but never assigned a value or used on the server side for the PGS v1 flow. It should be removed to improve code clarity.

Comment on lines +255 to +259
var currNum = int.Parse(incText.text);
currNum++;
incText.text = currNum.ToString();

StartCoroutine(PostScore());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using int.Parse can throw a FormatException if the text in incText is not a valid integer. This could happen if the UI element is somehow modified to be non-numeric. Using int.TryParse is a more robust way to handle this conversion.

        if (int.TryParse(incText.text, out var currNum))
        {
            currNum++;
            incText.text = currNum.ToString();

            StartCoroutine(PostScore());
        }
        else
        {
            Debug.LogError($"Failed to parse '{incText.text}' as an integer.");
        }

Copy link
Contributor

@natetrost natetrost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the apache license header to the new source files where applicable, then looks good to merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants