question

amedrycki avatar image
amedrycki asked

Google Player ID problem and possible GetPlayFabIDsFromGoogleIDs bug

I have successfully implemented the latest PlayFab SDK and Play Games Plugin v0.96 in our Unity Project.

The game startup procedure is the following:

  1. Login to PlayFab using a device ID
  2. Login to Google Play Game Services using Social.localUser.Authenticate
  3. Call GetAccountInfo to get basic player data from PlayFab (including linked Google ID)
  4. If the Google ID from GetAccountInfo is empty or different from the one obtained from Play Games Plugin we call GetPlayFabIDsFromGoogleIDs to check if the current Play Games ID is linked with other PlayFab account or not.
  5. Further actions including LinkGoogleAccount or LoginWithGoogleAccount depending on the result of GetPlayFabIDsFromGoogleIDs and the player's choice to resume progress.

So far I managed to successfully login the player to PlayFab, login to Play Games and link the accounts.

However, I have encountered two problems:

  1. The first problem is that for some Google accounts the PlayGamesPlatform.Instance.GetUserId(); returns a different player ID than the one retrieved from PlayFab with GetAccountInfo. The Player ID obtained from Play Games Plugin has letter 'g' plus 20 digits (i.e. 'g12345678901234567890') while the Google ID obtained from PlayFab consists of 21 digits (i.e. '123456789012345678901').
    I assume it's connected with this Google Play change, and PlayFab is storing the Google user ID instead of the Player ID that is returned by the Play Games Unity Plugin.
  2. If I feed the ID returned by the Google Play Games Plugin to GetPlayFabIDsFromGoogleIDs it returns an empty string instead of a PlayFab ID so I am unable to tell if the Play Games account is already linked to PlayFab or not which is crucial for the game startup procedure and PlayFab integration.

Is there any way to obtain the PlayFab ID from the new Google Player ID format ('g' + 20 digits)?

Or maybe you know how to obtain the Google user ID from the Play Services Plugin (21 digits without the 'g')?

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

brendan avatar image
brendan answered

What title ID are you using for your test, and can you confirm that you are using the auth code parameter in your login, as described in this guide (https://api.playfab.com/docs/tutorials/landing-players/sign-in-with-google)? Since you're using Social.localUser.Authenticate, it sounds like the case, but if you can confirm, that would be best.

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

amedrycki avatar image
amedrycki answered

Thanks for the quick reply.

The title ID I'm using to test is 4195 and the PlayFab account ID last used for testing is '64A27BADDA3E0757'.

However, the Server Auth Code is not the problem in my case. I am able to successfully get it by calling PlayGamesPlatform.Instance.GetServerAuthCode() and use it to LinkGoogleAccount with the PlayFab account. Everything works fine up until that point.

The problem starts only after I link the accounts, when I start the game for the second time - the GetAccountInfo returns a Google ID that is different from the one returned by the Google Play plugin. Also GetPlayFabIDsFromGoogleIDs returns an empty string when I pass the Google ID from the Google Play plugin to it.

My guess is that PlayFab stores the old G+ player ID while the Google Play Services plugin returns the new Gamer ID, similarly to the problem described here.

I attach the code I use to initialize and sign in to the Google Play Game Services below FYI:

private void Init()
{
    PlayGamesClientConfiguration.Builder configBuilder = new PlayGamesClientConfiguration.Builder();
    configBuilder.AddOauthScope("profile");
    PlayGamesClientConfiguration config = configBuilder.Build();
    PlayGamesPlatform.InitializeInstance(config);
    PlayGamesPlatform.DebugLogEnabled = true;
    PlayGamesPlatform.Activate();
}
public void Login(System.Action<bool> onFinish = null)
{
    Init(); // Make sure it's initialized first
    Social.localUser.Authenticate((bool success) =>
    {
        Log(success ? (
                "Logged in successfully. \n" +
                "User ID: '" + PlayGamesPlatform.Instance.localUser.id + "'\n" +
                "User Name: '" + PlayGamesPlatform.Instance.localUser.userName
            ) : "Failed to login.");
        // Cache server auth code after login
        GetServerAuthCode((result, code) =>
        {
            // DEBUG INFO:
            Log("Access Token: '" + PlayGamesPlatform.Instance.GetAccessToken() + "'");
            PlayGamesPlatform.Instance.GetIdToken((token) =>
            {
                Log("ID Token: '" + token + "'");
            });
            Log("Token: '" + PlayGamesPlatform.Instance.GetToken() + "'");
            Log("User Display Name: '" + PlayGamesPlatform.Instance.GetUserDisplayName() + "'");
            Log("User Email: '" + PlayGamesPlatform.Instance.GetUserEmail() + "'");
            Log("User ID: '" + PlayGamesPlatform.Instance.GetUserId() + "'");
            Log("Local User ID: '" + PlayGamesPlatform.Instance.localUser.id + "'");
            Log("Local User Name: '" + PlayGamesPlatform.Instance.localUser.userName + "'");
            Log("Social Local User ID: '" + Social.localUser.id + "'");
            Log("Social Local User Name: '" + Social.localUser.userName + "'");
                
            if (onFinish != null)
                onFinish(success);
        });
    });
}
public void GetServerAuthCode(System.Action<CommonStatusCodes, string> onFinish = null)
{
    PlayGamesPlatform.Instance.GetServerAuthCode((result, code) =>
    {
        if (result == CommonStatusCodes.Success || result == CommonStatusCodes.SuccessCached)
            _serverAuthCode = code;
        if (onFinish != null)
            onFinish(result, code);
    });
}
3 comments
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

brendan avatar image brendan commented ·

We use the Subject from the payload of the token we get from Google in exchange for the one you send to us (https://developers.google.com/identity/sign-in/web/server-side-flow). So that would be the one to use for lookup of the player and matching. I imagine they probably chose to do this for backwards compatibility, which would make sense, given that they've a very large number of titles active and wouldn't want one to no longer be able to reference their players by the consistent ID they'd used up to that point. Similarly, we can't really change to use a different ID on our side, as that would break existing live titles. So I'd recommend using that ID, even if it's the older version

0 Likes 0 ·
amedrycki avatar image amedrycki brendan commented ·

Unfortunately the only ID I am able to obtain from the latest Play Services plugin (using PlayGamesPlatform.Instance.GetUserId()) is the new player ID ('g12345678901234567890'). And it's different from the one stored on PlayFab server and doesn't work with GetPlayFabIDsFromGoogleIDs.

How should I get the proper ID to use with PlayFab then?

0 Likes 0 ·
brendan avatar image brendan amedrycki commented ·

Unfortunately, Google provides no mapping of the Google account ID (the one used for Google authentication) and the Google Play Games user ID. Further, their secure account authentication returns the Google account ID, and not the Google Play Games user ID. Their support team recommends that developers fill out this form if the new ID system is creating a blocking issue for their titles: https://docs.google.com/a/playfab.com/forms/d/e/1FAIpQLSfODa_yK2LATv_0czKx8TZrZZlyW4tnp9zGPGSXQX6qPsAycw/viewform.

We'd be very interested in any response you get from them on this topic, as their GPG ID system does create a serious disconnect that developers have to work around for the kind of lookup operation you're trying to accomplish. At the moment, the way to do this is by retrieving the user's Google account ID (https://developers.google.com/identity/sign-in/android/people).

0 Likes 0 ·
amedrycki avatar image
amedrycki answered

Thanks for the form link. I did fill it, but our project is still in the development phase, so there are no real users affected yet. Anyway, I will let you know should I get any reply from Google:

Should anyone encounter a similar problem I managed to implement a workaround by using GPG plugin v 0.9.36 and calling Google's raw REST API to obtain the proper user ID with the following piece of code:

private sealed class GoogleAPI_UserInfo
{
	public string id;
	public string email;
	public bool verified_email;
	public string name;
	public string given_name;
	public string family_name;
	public string link;
	public string picture;
	public string gender;
	public string locale;
}

private GoogleAPI_UserInfo _userInfo = null;
public string UserIdGPlus { get { return (_userInfo == null || _userInfo.id == null) ? "" : _userInfo.id; } }

private void GetGPlusUserId(System.Action onFinish = null)
{
    // Either of those URLs should work
    string url = "https://www.googleapis.com/oauth2/v2/userinfo";
    //string url = "https://www.googleapis.com/userinfo/v2/me";

    bool tokenInHeader = true;

    WWW www = null;
    if (tokenInHeader)
    {
        Dictionary<string, string> headers = new Dictionary<string, string>();
        //headers.Add("Host", "www.googleapis.com");
        //headers.Add("Content-length", "0");
        //headers.Add("Content-type", "application/json");
        headers.Add("Authorization", "OAuth " + PlayGamesPlatform.Instance.GetToken());

        byte[] postData = null;

        www = new WWW(url, postData, headers);
    }
    else
        www = new WWW(url + "?access_token=" + PlayGamesPlatform.Instance.GetToken());

    StartCoroutine(Crtn_WaitForWWW(www, (error, text) =>
    {
        if (error == null)
        {
            if (!string.IsNullOrEmpty(text))
            {
                _userInfo = JsonUtility.FromJson<GoogleAPI_UserInfo>(text);
                if (_userInfo != null)
                    Log("Retrieved OAuth user info with ID '" + _userInfo.id + "'");
            }
        }
        if (onFinish != null)
            onFinish();
    }));
}

private IEnumerator Crtn_WaitForWWW(WWW www, System.Action<string, string> onFinish = null)
{
    yield return www;
    if (www.error == null)
        Log("Successfully retrieved URL.");
    else
        Log("Got error while trying to retrieve the URL: " + www.error);
    if (onFinish != null)
        onFinish(www.error, www.text);
}

However this is only a temporary solution as in the newest Play Services plugin (v0.9.38a) there is no GetToken() method and they say that the client app should get the access token to call the REST API from the backend server after sending the server auth code to it.

Another idea is to use PlayFab's CustomID to manually store the GPG ID after linking, but that blocks me from using a real CustomID should we decide to implement one.

Using the GenericID with a special name (like "GPG") for that purpose would be a better choice but in this case I don't see the option to force-relink an ID already assigned to other PlayFab account.

Please let me know if you have any other ideas or if there is a new PlayFab API version that can store and search the GPG ID as well.

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.