Photon Secret Key is visible in Error Log?

The whole answer can be found below.

Please note: The Photon forum is closed permanently. After many dedicated years of service we have made the decision to retire our forum and switch to read-only: we've saved the best to last! And we offer you support through these channels:

Try Our
Documentation

Please check if you can find an answer in our extensive documentation on PUN.

Join Us
on Discord

Meet and talk to our staff and the entire Photon-Community via Discord.

Read More on
Stack Overflow

Find more information on Stack Overflow (for Circle members only).

Write Us
an E-Mail

Feel free to send your question directly to our developers.

Photon Secret Key is visible in Error Log?

AllFatherGray
2020-09-22 23:42:40

When I send an Error Code with a Non-0 value via web hook the room fails to be created, which is in line with the documentation.

However, I see that the player logs store the secret key. I tried turning off ErrorInfo and other log level shenanigans but it is still visible, even in a standalone, non debug build.

How can I prevent a user from seeing this key?
I asked the question here along with some others:

https://community.playfab.com/questions/44938/is-there-an-updated-guide-for-async-photon-unity-t.html

Thanks in advance.

Comments

JohnTube
2020-09-23 00:38:16

Hi @AllFatherGray,

I just replied on PlayFab forums.

How can I prevent a user from seeing this key?

You can't avoid exposing PlayFab's Secret Key in client logs currently.

AllFatherGray
2020-09-24 04:33:32

@JohnTube said:
Hi @AllFatherGray,

I just replied on PlayFab forums.(Quote)
You can't avoid exposing PlayFab's Secret Key in client logs currently.

Thanks for tge response, I asked another question in that forum about names and the key.
Could you please give it a look?

JohnTube
2020-09-24 14:06:18

I think there is a limit to the number of the nested/chained replies in the PlayFab forums, I can't reply to your last comment/reply, so let's continue here since it's about Photon anyway and this Vanilla forum is more convenient and easy to use.
Here is a link to your last questions for reference.

Is there a way to force webhooks? I don't see how I can enforce rules on a user that can avoid the server logic.

No there is none. After all with or without webhooks forwarding for SetProperties or RaiseEvent you can't cancel or prevent the operation from PlayFab CloudScript or any other WebHooks endpoints implementation.

What work-arounds are available for the Secret Key?
Can you get someone to explain it?

Because right now it seems like a person could read the logs and send calls outside of the ecosystem we are developing. Unless I am missing something this seems like a big issue. From my understanding, a person could:

  1. Download a game that is using Photon Unity.
  2. Trigger an error in the Path webhooks (Like in PathCreate).
  3. Obtain the secret-key from the player.log.
  4. Make calls to the Server using the key.

Is this okay?

First I'm here (the "someone") to explain all this and answer any question about it.
Photon offers a way to protect from this using AuthCookie: the authentication provider returns a secure object per user, this object is sent in PathCreate, PathJoin automatically and always and can be sent on demand by client request in webhooks per WebFlag.SendAuthCookie for PathGameProperties, PathEvent and PathLeave. This can be used in SetProperties, RaiseEvent and LeaveRoom client methods calls.
Unfortunately PlayFab does not offer this out of the box but you can make it work with PlayFab nevertheless. I tested it myself.
See here for more info (this is not officially supported).

AllFatherGray
2020-09-25 02:57:43

@JohnTube wrote: »

First I'm here (the "someone") to explain all this and answer any question about it.
Photon offers a way to protect from this using AuthCookie: the authentication provider returns a secure object per user, this object is sent in PathCreate, PathJoin automatically and always and can be sent on demand by client request in webhooks per WebFlag.SendAuthCookie for PathGameProperties, PathEvent and PathLeave. This can be used in SetProperties, RaiseEvent and LeaveRoom client methods calls.
Unfortunately PlayFab does not offer this out of the box but you can make it work with PlayFab nevertheless. I tested it myself.
See here for more info (this is not officially supported).

Before I continue, Again, thanks you've been a great help
So you are saying that I should call this CustomAuth function in PathCreate and the other methods?
And then I compare the returned auth to the one in the args?

For example:
PathCreate(args)
{
var result = CustomAuth(args);
//compare here?
}
I notice you have the method connected to handler.CustomAuth, could I remove the handler variable and keep this method for server side checks? So, handlers.CustomAuth -> CustomAuth

Lastly I asked about the name in the playfab discussion (below):

When joining a room in photon if the player name is the same as the player who created the room will photon permit the join or block it? I believe that when you’re not authenticating with photon it will be blocked, but does that behavior persist after we obtain a photon auth token?

Must I force unique names or will the auth be enough?
Or should I combine them for unique names like this: {Username}{PlayfabID} to make each name unique?

EDIT:
My email for the forum and actual PUN account don't match and I don't get notifications.
How do I change this forums email?

JohnTube
2020-09-25 10:06:27

So you are saying that I should call this CustomAuth function in PathCreate and the other methods?
And then I compare the returned auth to the one in the args?

No I'm saying you should use a different way to use custom authentication with PlayFab, different from the official integration.
So instead of URL format:
https://{PlayFabTitleId}.playfabapi.com/photon/authenticate
use:
https://{PlayFabTitleId}.playfablogic.com/webhook/1/prod/{PhotonSecretKey}/CustomAuth
and from the client:

AuthenticationValues authValues = new AuthenticationValues();  
authValues.AuthType = CustomAuthenticationType.Custom;  
authValues.AddAuthParameter("UserId", userId); // PlayFabId  
authValues.AddAuthParameter("AppId", appId); // Photon AppId  
authValues.AddAuthParameter("Ticket", ticket); // PlayFab auth session ticket  
authValues.AddAuthParameter("Token", token); // Photon auth token obtained from PlayFab via client API call GetPhotonAuthenticationToken  
authValues.AddAuthParameter("AppVersion", appVersion); // Photon AppVersion

and do not forget to include the CustomAuth handler in your CloudScript revision.
The idea behind this is to use a custom WebHooks/WebRPC endpoint for custom auth.
So the custom handler "CustomAuth" (you may rename handler and update URL accordingly) will be called by Photon Server when doing authentication instead of the normal endpoint.
And inside CustomAuth handler we do the actual authentication and more (get user info, compare AppVersion, return AuthCookie, return Data, etc.)
You don't have to do all this, I shared the code I had in full but what you need is just AuthCookie and you decide whatever sensitive/secret data per user you put in there to make sure WebHooks calls are incoming from a trusted user and not some hacker.

When joining a room in photon if the player name is the same as the player who created the room will photon permit the join or block it? I believe that when you’re not authenticating with photon it will be blocked, but does that behavior persist after we obtain a photon auth token?

Must I force unique names or will the auth be enough?
Or should I combine them for unique names like this: {Username}{PlayfabID} to make each name unique?

I think this is more than one question/thing. So I will try to clarify each point separately:

  • when you authenticate using PlayFab the Photon UserId will be set to the PlayFabId. So the authentication is enough and will do this for you.
  • by default each actor joined to a room must have a unique UserId, so if a user joins a room with UserId X no other user can join the same room with the same UserId X. For example if someone connects with the same account on multiple devices he can connect to Photon but cannot join the same room from more than one device.

My email for the forum and actual PUN account don't match and I don't get notifications.
How do I change this forums email?

Send an email to [email protected] from the account you want to change (current forum account) and include the new email that you want to use for the forum to match the account where you have PUN app.

AllFatherGray
2020-09-25 12:31:38

Just to confirm,, If I authenticate using the above method I shouldn't have to worry about the exposed secret key? I guess I am asking what is different from getting an auth key the other way?
How does this auth key differ?

Ah, so the names can match because of the user ID being different? Good to know.

I will contact the email to change my address, thanks.

JohnTube
2020-09-25 14:05:59

If I authenticate using the above method I shouldn't have to worry about the exposed secret key? I guess I am asking what is different from getting an auth key the other way?
How does this auth key differ?

When you use this method you can return AuthCookie. When you properly use AuthCookie you can make sure, from PlayFab webhooks handlers (except PathClose) that the webhook is safe (not triggered via someone who managed to get the WebHooks URL and secret key for your title).
So you still need to worry as you can't prevent WebHooks URL or secret key from leaking but you asked for a workaround.
The method I suggest is a wrapper around default approach suggested in official integration which adds missing features, AuthCookie being one of those.

Ah, so the names can match because of the user ID being different? Good to know.

I'm having trouble following. Are you asking about Photon Nickname or UserId? Nickname are free and there is no constraint. UserId, only one is allowed per room: all actors need to have different UserIds. You can't join a room if there is an actor with the same UserId already joined. This does not apply to Nicknames.

AllFatherGray
2020-09-25 15:38:09

Yes I was talking about both the nickname and userid. I wanted to know how player uniqueness was done.
Thanks for clarifying it.

So how would I call this CustomAuth Method? Do I use Execute cloud script in the client?
Do I send the AuthenticationValues object as the arg? What is AuthenticationValues?
Where would I call the CustomAuth() method down below?

 /*  
     * Step 3  
     * This is the final and the simplest step. We create new AuthenticationValues instance.  
     * This class describes how to authenticate a players inside Photon environment.  
     */  
    private void AuthenticateWithPhoton(GetPhotonAuthenticationTokenResult obj)  
    {  
        LogMessage("Photon token acquired: " + obj.PhotonCustomAuthenticationToken + "  Authentication complete.");

        //We set AuthType to custom, meaning we bring our own, PlayFab authentication procedure.  
        var customAuth = new AuthenticationValues { AuthType = CustomAuthenticationType.Custom };  
        //We add "username" parameter. Do not let it confuse you: PlayFab is expecting this parameter to contain player PlayFab ID (!) and not username.  
        customAuth.AddAuthParameter("username", _playFabPlayerIdCache);    // expected by PlayFab custom auth service

        //We add "token" parameter. PlayFab expects it to contain Photon Authentication Token issues to your during previous step.  
        customAuth.AddAuthParameter("token", obj.PhotonCustomAuthenticationToken);

        //We finally tell Photon to use this authentication parameters throughout the entire application.  
        PhotonNetwork.AuthValues = customAuth;  
    }

AllFatherGray
2020-09-25 16:54:28

Ok I took a look at the doc and your earlier post and it seems that I will receive the cookie when I log on once Photon attempts to call the custom auth url. So I don't call CustomAuth myself, correct?

So, once the call is finished and I return the cookie, is the Cookie automatically set in the client?

"AuthCookie: also called secure data, is a JSON object returned by the web service but will not be accessible from client side as it will be embedded in the encrypted token received. It could be sent later with Webhook or WebRPC HTTP requests."

I saw this in the document, can you clarify it? Does it mean the cookie will be embeded in the args.Token?

JohnTube
2020-09-28 11:20:52

Hi @AllFatherGray,

Ok I took a look at the doc and your earlier post and it seems that I will receive the cookie when I log on once Photon attempts to call the custom auth url. So I don't call CustomAuth myself, correct?

Correct, just replace Auth URL in the dashboard and Photon Server (not the client) will automatically call the handler when the client connects with proper authentication values (type and parameters) configured.

I saw this in the document, can you clarify it?

The client does not need to know the AuthCookie as the whole point of this is to not trust the client. Only the server saves this and will resend it to your web server in some webhooks always/automatically and for some webhooks it requires the client to initiate that via webflag.>Does it mean the cookie will be embeded in the args.Token?

No. There is no default args.Token in webhooks or webRPCs. args.Token in the suggested workaround inside CustomAuth handler is what the client sends, which is the actual Photon auth token via PlayFab client API call (you can rename it to whatever you want btw, it's a query string parameter key name).

AllFatherGray
2020-09-28 15:31:48

@JohnTube
Hello, hope you had a good weekend.

The client does not need to know the AuthCookie as the whole point of this is to not trust the client. Only the server saves this and will resend it to your web server in some webhooks always/automatically and for some webhooks it requires the client to initiate that via webflag.

So I simply return a unique string as AuthCookie on this line? Where secret is in the line below? The cookie is saved in the Photon Server?

return { ResultCode: 1, UserId: userId, Nickname: nickname/*, Data: userInfo*//*, AuthCookie: secret*/ };

From that point onwards, do I just check 'args.AuthCookie' on all subsequent wehook calls?
If so, what do I check for?

I see that args.Ticket is used in the CustomAuth, how do I get that down below?

customAuth.AddAuthParameter("ticket", /* What goes here from playfab? */);

Because I see this:

// authValues.AddAuthParameter("Ticket", ticket); // PlayFab auth session ticket

Where do I get that from?

Thanks in advance.

JohnTube
2020-09-28 17:07:45

So I simply return a unique string as AuthCookie on this line?

not a string but a JSON object with one level depth (no nested objects).
example:

var secret = { "key": "value", "k": 1, "kk": true };

Where do I get that from?

string ticket = PlayFabSettings.staticPlayer.ClientSessionTicket  
customAuth.AddAuthParameter("ticket", ticket);

AllFatherGray
2020-09-28 18:16:20

@JohnTube

@JohnTube wrote: »

So I simply return a unique string as AuthCookie on this line?

not a string but a JSON object with one level depth (no nested objects).
example:

var secret = { "key": "value", "k": 1, "kk": true };

Do I ever check for the cookie explicitly in the webhooks? Or is it autochecked in the server calls?
And what should I put in the secret - a random key?
Would the below code work as an acceptable AuthCookie/secret?

var secret = { "key": "some random GUID" };

Lastly,
Does the cookie expire? I know Playfab tickets expire in 24 hours. should I expect the same for the cookie?

JohnTube
2020-09-29 12:29:14

Do I ever check for the cookie explicitly in the webhooks? Or is it autochecked in the server calls?
And what should I put in the secret - a random key?

You do the check yourself in each CloudScript handler, if AuthCookie is expected but missing or if it's there but its value is wrong then assume this webhook was not sent by Photon Server or was sent using a malicious client (through Photon Server, the latter is probably a modified client that does not set proper webflag).

Would the below code work as an acceptable AuthCookie/secret?

yes sure. it should be something per user, per session and that you can keep track of from CloudScript to compare it to with received values.

Does the cookie expire? I know Playfab tickets expire in 24 hours. should I expect the same for the cookie?

AuthCookie is per session. It's gone when the client is disconnected from Photon Servers. But it does not expire otherwise.

AllFatherGray
2020-09-29 16:47:16

@JohnTube

You do the check yourself in each CloudScript handler, if AuthCookie is expected but missing or if it's there but its value is wrong then assume this webhook was not sent by Photon Server or was sent using a malicious client (through Photon Server, the latter is probably a modified client that does not set proper webflag).

Ah ok, so they can't see the cookie because it's saved on the server but it is sent anyways.
But I should still check the secret to validate.If they set webflags to false the AuthCookie won't be sent, correct? In that case I would also check if the AuthCookie is null/undefined?

And when you mean do a check you mean we must validate the Secret any way we want?

Here is an example to make sure I have it right:
A user gets an AuthCookie from us and we save a copy of the cookie some whereelse.
Photon saves the cookie as well.

When a legal user calls:
We compare the auth cookie to our store to the one Photon re-sent.

Okay here are the different outcomes:

1.
If a hacker sends a fake cookie:
The Cookie will not match or be invalid.

2.
If hacker makes a call to the url without a fake cookie:
the AuthCookie is undefined or missing so we ban/penalty user.

3.
If a hacker set webflag to false (like in (SetRoomProperties or RaiseEvent):
the AuthCookie is undefined or missing so we ban/penalty user.

All of this is possible because the AuthCookie is never sent to the client but is actually held onto the Photon Server. So we only expect args.AuthCookie to match if it's still a valid cookie with what we stored.

Are the 3 examples and the above correct?

Lastly, How would you reccomend I store/check the Cookie?

JohnTube
2020-09-30 11:11:12

But I should still check the secret to validate.If they set webflags to false the AuthCookie won't be sent, correct? In that case I would also check if the AuthCookie is null/undefined?

Correct, yes.

And when you mean do a check you mean we must validate the Secret any way we want?

It means you compare and check in your code according to how AuthCookie format/structure is and whether or not it's expected/used in all webhooks where applicable.

Are the 3 examples and the above correct?

Yes. When cookie is not found or invalid, I would not be so sure to punish the user as someone else may be using a fake/someone else's UserId/PlayFabId. So I would only log this, report it and ignore any extra logic/processing.

Lastly, How would you reccomend I store/check the Cookie?

It's been a long time since I used PlayFab and it has change a lot. Player Data (Title) maybe?

Just a note/reminder:
PathClose cannot have an AuthCookie, so this one you should check validity by making sure all the args are there as expected and their values are valid. Then see if the GameId closed is currently marked as open/ongoing.

Recommendation:
I would change ALL handlers names (respectively webhooks' pathes) EXCEPT PathClose.
This way if some hacker knows Photon/PlayFab at least you can hope he does not guess all webhook paths by using default handers' names.

AllFatherGray
2020-09-30 15:06:30

@JohnTube

Then see if the GameId closed is currently marked as open/ongoing.

Ok, how do I check if the GameID is open/closing? Is there an API call to do this?

JohnTube
2020-09-30 16:13:18

No.
The idea is to not trust webhooks.

Disclaimer: you don't have to follow everything I say but only what works for you and you can start simple then add changes incrementally later on.
I might sound paranoid.

When PathCreate is called and validated (and allowed via ResultCode: 0):
you try to add the GameId to a list of open games. (e.g. SharedGroupData)
if such GameId exists for the same enviornement (Region, AppId, AppVersion, etc.) return ResultCode != 0.
you add the UserId / ActorNr pair to the list of actors joined to the game.
When PathJoin is called and validated (args checks & AuthCookie) you add the UserId / ActorNr pair to the list of actors joined to the game.
When PathLeave is called and validated (args checks & AuthCookie):
if ActorNr == -1, ignore/skip, this means the actor never joined successfully.
if IsInactive == true, mark the actor as inactive in the previously saved entry in PathJoin.
if IsInactive == false, remove the actor from the list.
When PathClose is called:
try to find that GameId in that list.
count the active actors (it should be zero).
if Type == "Close" remove the entry
if Type == "Save", save the State and optionally remove the GameId entry and actors list.

AllFatherGray
2020-09-30 17:56:41

@JohnTube Thank you so much! This thread clears up most of my concerns.
If I have anymore questions I will ask again but this is good for now.

count the active actors (it should be zero).

Oh I guess the actorlist is contained in the state or is it in game.args?
Or you mean to check what is in the sharedgroup data?

EDIT:
I just thought of something!
How can I enforce the room names for the CreateRoom?

Would I have to resort to fetching and checking preapproved GUIDs from a secure source?

EX:

In the cloud I save the 'next' room name for a user.
When they attempt to make a room they must first ask for this new name.
If the room they attempt to create with args.Type == Create is different from the value, I throw an error.
After that, I save a new roomname for the next create that user might call.

JohnTube
2020-09-30 18:50:14

Or you mean to check what is in the sharedgroup data?

Yes.
For room names simply do not set one (send null) in CreateRoom method and let Photon Server generate a GUID for you.
If you want to use custom room names see this.

AllFatherGray
2020-09-30 19:46:54

@JohnTube

For room names simply do not set one (send null) in CreateRoom method and let Photon Server generate a GUID for you.

I was talking about the case where a user sets the name purposefully like:
CreateRoom("fakename");/// they removed null and put this instead.
So the call is legal but the name is what I am worried about.

My example of how to solve it is:

Would I have to resort to fetching and checking preapproved GUIDs from a secure source?

EX:

In the cloud I save the 'next' room name for a user.
When they attempt to make a room they must first ask for this new name.
If the room they attempt to create with args.Type == Create is different from the value, I throw an error.
After that, I save a new roomname for the next create that user might call.

Would the above be an acceptable way to keep the names under my control?

AllFatherGray
2020-10-01 13:13:26

@JohnTube

I also forgot to ask:

Are there any plans to integrate the gits or workarounds you made into an official format?

JohnTube
2020-10-02 12:47:01

Would I have to resort to fetching and checking preapproved GUIDs from a secure source?

Would the above be an acceptable way to keep the names under my control?

So you want to make sure the client calling CreateRoom is from genuine client? Or you want to allow custom room names no matter what?
In either cases I think you now have enough knowledge about how all this works and there are enough features to figure out.

Are there any plans to integrate the gits or workarounds you made into an official format?

No sorry. At some point we will have an updated webhooks version but no ETA and not soon.

AllFatherGray
2020-10-02 16:33:18

@JohnTube

Yes I meant something else.
Like a is genuine but alters the name of the create room request from null to something else.
So they correctly login but maliciously change the code and
CreateRoom(null) turns into -> CreateRoom("really different name")

So my suggestion was to generate a name for a user on the server and save it and then send it to them and only let a create room request succeed if the name/gameid of the room matched the name I generated on the server. Does that sound like a good approach? I swear this is my last chain of questions for now!

No sorry. At some point we will have an updated webhooks version but no ETA and not soon.

Okay, I'll just keep looking at the news from Photon.

Again, thank you so much for answering all the questions these past few weeks!

AllFatherGray
2020-10-21 00:01:32

@JohnTube

I switched my auth to the URL you described earlier and I get this error now:

OperationResponse 230: ReturnCode: 32755 (Custom authentication got empty response string.). Parameters: {} Server: NameServer Address: ns.exitgames.com:5058  
UnityEngine.Debug:LogError(Object)  
Photon.Realtime.LoadBalancingClient:DebugReturn(DebugLevel, String) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:2393)  
Photon.Realtime.LoadBalancingClient:OnOperationResponse(OperationResponse) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:2467)  
ExitGames.Client.Photon.PeerBase:DeserializeMessageAndCallback(StreamBuffer)  
ExitGames.Client.Photon.EnetPeer:DispatchIncomingCommands()  
ExitGames.Client.Photon.PhotonPeer:DispatchIncomingCommands()  
Photon.Pun.PhotonHandler:Dispatch() (at Assets/Photon/PhotonUnityNetworking/Code/PhotonHandler.cs:208)  
Photon.Pun.PhotonHandler:FixedUpdate() (at Assets/Photon/PhotonUnityNetworking/Code/PhotonHandler.cs:142)

It works when I put the auth URL back to:

https://{PlayFabTitleId}.playfabapi.com/photon/authenticate

Is there something I missed?

JohnTube
2020-10-21 09:30:23

Is there something I missed?

according to the error message:

Custom authentication got empty response string.

it looks like you need to check the response.
you could do it from a browser or using Postman to test that it works as expected.

AllFatherGray
2020-10-21 12:53:19

@JohnTube

For reference I am using this git:
https://gist.github.com/JohnTube/122568b1183db131d50f5f17935ee21a

it looks like you need to check the response.
you could do it from a browser

How do I check it from the browser?

JohnTube
2020-10-21 17:50:51

forget the browser as it's not sending POST. I apologize about mentioning it.

please use postman to fake the request the Photon Server is going to send to PlayFab and make sure you have a returned JSON response as expected by Photon.

use the URL of webhooks + handler name.
example:
https://{{PlayFabTitleId}}.playfablogic.com/webhook/1/prod/{{PhotonSecretKey}}/CustomAuth
send POST with payload, example (replace values and add missing for test)

{  
   AppId: "<photonAppId>",  
   UserId: "<userId>",  
}

you should get a JSON response like, example:

{  
   ResultCode: 2   
   Message: "Token is undefined"  
}

this is how I test it (values between {} are replaced as they are environment variables)

AllFatherGray
2020-10-21 19:05:41

@JohnTube
So I tried making a post request to https://{{PlayFabTitleId}}.playfablogic.com/webhook/1/prod/{{PhotonSecretKey}}/CustomAuth with the body as you suggested and received

{  
    "code": 500,  
    "status": "InternalServerError",  
    "error": "InternalServerError",  
    "errorCode": 1110,  
    "errorMessage": "An unexpected error occured while processing the request.",  
    "errorHash": "5545bfb177b6874beb917291c6901c7d",  
    "errorDetails": {  
        "RequestID": [  
            "b6d2cc8e4c844b4e90a2f4b0e740f2a5"  
        ]  
    }  
}

JohnTube
2020-10-21 21:06:19

Make sure to use POST not GET and to send body content as raw, JSON
and also format message properly it should be proper valid correct JSON.
see the screenshot in my previous message.

AllFatherGray
2020-10-21 22:05:27

@JohnTube

Thanks, I was missing the JSON option.
Now I've correctly received a response (200 OK) from Postman.

What are the next steps to get this behaviour in Unity?

The CustomAuth fails when I attempt to connect via:
PhotonNetwork.ConnectUsingSettings();

JohnTube
2020-10-22 07:16:05

what is the error message in OnCustomAuthenticationFailed?
also share the client code and what you expect from the CloudScript.

AllFatherGray
2020-10-22 12:49:35

@JohnTube

EDIT:
I was using this as a guide:
https://docs.microsoft.com/en-us/gaming/playfab/sdks/photon/quickstart

I get this before the error occurs.

PlayFab + Photon Example: Photon token acquired: qc4ukeqedcmgxdqutwibq8ie8mr4ipntne7bc64nte7tbocdp1  Authentication complete.

Then, I get the error below when I try to connect.
END EDIT---------

This is the error message I get.

OperationResponse 230: ReturnCode: 32755 (Custom authentication got empty response string.). Parameters: {} Server: NameServer Address: ns.exitgames.com:5058  
UnityEngine.Debug:LogError(Object)  
Photon.Realtime.LoadBalancingClient:DebugReturn(DebugLevel, String) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:2393)  
Photon.Realtime.LoadBalancingClient:OnOperationResponse(OperationResponse) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:2467)  
ExitGames.Client.Photon.PeerBase:DeserializeMessageAndCallback(StreamBuffer)  
ExitGames.Client.Photon.EnetPeer:DispatchIncomingCommands()  
ExitGames.Client.Photon.PhotonPeer:DispatchIncomingCommands()  
Photon.Pun.PhotonHandler:Dispatch() (at Assets/Photon/PhotonUnityNetworking/Code/PhotonHandler.cs:208)  
Photon.Pun.PhotonHandler:FixedUpdate() (at Assets/Photon/PhotonUnityNetworking/Code/PhotonHandler.cs:142)  

In the cloudscript I am doing this as a quick test:

 return { ResultCode: 1, AuthCookie:  { secret: "secret" } };

This is the code in the client:

LogMessage("Photon token acquired: " + obj.PhotonCustomAuthenticationToken + "  Authentication complete.");

        //We set AuthType to custom, meaning we bring our own, PlayFab authentication procedure.  
        var customAuth = new AuthenticationValues();  
        customAuth.AuthType = CustomAuthenticationType.Custom;  
        //We add "username" parameter. Do not let it confuse you: PlayFab is expecting this parameter to contain player PlayFab ID (!) and not username.  
        customAuth.AddAuthParameter("username", _playFabPlayerIdCache);    // expected by PlayFab custom auth service  
        customAuth.AddAuthParameter("UserId", _playFabPlayerIdCache);

        //We add "token" parameter. PlayFab expects it to contain Photon Authentication Token issues to your during previous step.  
        customAuth.AddAuthParameter("Token", obj.PhotonCustomAuthenticationToken);

        string ticket = PlayFabSettings.staticPlayer.ClientSessionTicket;  
        customAuth.AddAuthParameter("Ticket", ticket);

        customAuth.AddAuthParameter("AppVersion", PhotonNetwork.PhotonServerSettings.AppSettings.AppVersion);  
        customAuth.AddAuthParameter("AppId", PhotonNetwork.PhotonServerSettings.AppSettings.AppIdRealtime);  
        //We finally tell Photon to use this authentication parameters throughout the entire application.  
        PhotonNetwork.AuthValues = customAuth;  

Later, I try to join photon using:

PhotonNetwork.PhotonServerSettings.AppSettings.AppVersion = "1.0";  
PhotonNetwork.PhotonServerSettings.AppSettings.FixedRegion =  "us";  
PhotonNetwork.ConnectUsingSettings();

The call to Postman works. I get the response from cloudscript.
And if I change the URL on the PhotonDashboard back to the old version (https://{title}.playfabapi.com/photon/authenticate) , it works as expected.

So only this new URL in Photon Unity gives me the error.

Versions of software:

Unity : 2019.4.12f1
PUN : 2.22
Photon lib : 4.1.4.5

JohnTube
2020-10-22 23:01:35

sorry my bad
we should send POST custom auth request.
so we should set a payload.

I went back to the project I used to test this and found out how I did it back then (it's PUN Classic as PUN 2 did not exist back then but it works in the same way, this is irrelevant here):

         /*  
    * Step 2  
    * We request Photon authentication token from PlayFab.  
    * This is a crucial step, because Photon uses different authentication tokens  
    * than PlayFab. Thus, you cannot directly use PlayFab SessionTicket and  
    * you need to explicitely request a token. This API call requires you to   
    * pass Photon App ID. App ID may be hardcoded, but, in this example,  
    * We are accessing it using convenient static field on PhotonNetwork class  
    * We pass in AuthenticateWithPhoton as a callback to be our next step, if   
    * we have acquired token succesfully  
    */  
    private void RequestPhotonToken(LoginResult obj)  
    {  
        LogMessage("PlayFab authenticated. Requesting photon token...");

        //We can player PlayFabId. This will come in handy during next step  
        _playFabPlayerIdCache = obj.PlayFabId;  
        _playFabSessionTokenCache = obj.SessionTicket;

        PlayFabClientAPI.GetPhotonAuthenticationToken(new GetPhotonAuthenticationTokenRequest()  
        {  
            PhotonApplicationId = PhotonNetwork.PhotonServerSettings.AppID  
        }, AuthenticateWithPhoton, OnPlayFabError);  
    }

     /*  
     * Step 3  
     * This is the final and the simplest step. We create new AuthenticationValues instance.  
     * This class describes how to authenticate a players inside Photon environment.  
     */  
    private void AuthenticateWithPhoton(GetPhotonAuthenticationTokenResult obj)  
    {  
        LogMessage("Photon token acquired: " + obj.PhotonCustomAuthenticationToken + "  Authentication complete.");

        //We set AuthType to custom, meaning we bring our own, PlayFab authentication procedure.  
        var customAuth = new AuthenticationValues { AuthType = CustomAuthenticationType.Custom };

        ////We add "username" parameter. Do not let it confuse you: PlayFab is expecting this parameter to contain player PlayFab ID (!) and not username.  
        //customAuth.AddAuthParameter("username", _playFabPlayerIdCache);    // expected by PlayFab custom auth service

        ////We add "token" parameter. PlayFab expects it to contain Photon Authentication Token issues to your during previous step.  
        //customAuth.AddAuthParameter("token", obj.PhotonCustomAuthenticationToken);

        Dictionary<string, object> data = new Dictionary<string, object>(5);  
        data["UserId"] = _playFabPlayerIdCache;  
        data["AppId"] = PhotonNetwork.PhotonServerSettings.AppID;  
        data["AppVersion"] = string.Format("{0}_{1}", this.appVersion, PhotonNetwork.versionPUN); //PhotonNetwork.networkingPeer.AppVersion;  
        data["Ticket"] = _playFabSessionTokenCache;// + "x";  
        data["Token"] = obj.PhotonCustomAuthenticationToken;  
        customAuth.SetAuthPostData(data);

        //We finally tell Photon to use this authentication parameters throughout the entire application.  
        PhotonNetwork.AuthValues = customAuth;  
        PhotonNetwork.ConnectUsingSettings(appVersion);  
    }

replace the dictionary key values with your own.

AllFatherGray
2020-10-23 00:34:07

@JohnTube

I still have an issue but your advice to switch to POST did the trick, I had to change:

data["AppVersion"] = string.Format("{0}_{1}", this.appVersion, PhotonNetwork.versionPUN);

because it was throwing errors. versionPUN didn't exist.

I changed it to:

     data["AppVersion"] = string.Format("{0}_{1}", PhotonNetwork.PhotonServerSettings.AppSettings.AppVersion, PhotonNetwork.PunVersion);

And I get a successful response when I return code == 1 in the CustomAuth.

However, when I add the rest of the code in the git, I receive errors:

OperationResponse 230: ReturnCode: 32755 (undefined: undefined). Parameters: {} Server: NameServer Address: ns.exitgames.com:5058

I have debug messages before and after:

        let appId = args.AppId;  
        let userId = args.UserId;  
        let appVersion = args.AppVersion;

But the error seems to occur before the assignments above are finished. Any ideas?

AllFatherGray
2020-10-23 04:06:24

@JohnTube

Found the issues!

First, all TitleWriteEvents must not contain special-characters and have a length < = 64.
Second, although Photon uses a key 'ResultCode' the AuthResponse uses 'resultCode'

So:

if (authRes.ResultCode !== 1)

Should become:

if (authRes.resultCode !== 1)

Doing that fixed bug.

EDIT: The fix above is for:
https://gist.github.com/JohnTube/122568b1183db131d50f5f17935ee21a

AllFatherGray
2020-11-06 02:38:58

@JohnTube

Hello, I have another question, I get null errors when using URLTags like here:
https://doc.photonengine.com/en-us/pun/v2/gameplay/web-extensions/webrpc#response

What is the proper why to use this? I am looking to use the {Region} tag.
Is this feature deprecated? I notice the Region key is already present in 'args' parameter despite the documents.

EDIT:
Earlier you said:

"Just a note/reminder:
PathClose cannot have an AuthCookie, so this one you should check validity by making sure all the args are there as expected and their values are valid. Then see if the GameId closed is currently marked as open/ongoing."

How do I check if a game is "open/ongoing"?

JohnTube
2020-11-08 23:33:06

Hi @AllFatherGray,

hmm

URL tags may not be officially supported by PlayFab integration.
I will have to test possible workarounds.
Yes Region is a URL tag and a webhooks/webRPC arg.


To check if a game is on going, simply mark it or flag as on going or open in your backend (PlayFab in this case) when you get PathCreate.

AllFatherGray
2020-11-09 02:36:06

@JohnTube

Ok, I will check in here in a week the latest.

JohnTube
2020-11-09 22:44:57

The only hacky workaround is to use URL tags as handler name.
Other than that you can't use them in the path as URL slug nor query string parameters.
So I would say that you should avoid them as you can live without them and if you want you can what you need from the client anyway as POST payload (JSON property).

AllFatherGray
2020-11-11 16:55:11

@JohnTube

I couldn't get PathClose to trigger when a room closed.
Is this still a feature?

JohnTube
2020-11-13 09:26:08

Hi @AllFatherGray,

PathClose is triggered when a room is removed from Photon servers.
A room is removed from Photon servers after it becomes empty and the EmptyRoomTtl (milliseconds) expires.

AllFatherGray
2020-11-17 05:52:46

@JohnTube

Thanks! It was an error with a tutorial that had 'RoomClose' when Playfab had 'RoomClosed'

Back to top