Authentication
User Ids and Player Names
The UserId uniquely identifies a player account across sessions and devices.
It drives everything identity-based in the SDK: matchmaking slot reservations, friend queries, rejoining a room after a disconnect and the PublishUserId room option.
If you do not supply one, the server assigns a random UserId at connect, fine for testing, but stable ids are a prerequisite for friends and rejoin.
The player name is a display name, set through ConnectOptions.Username at connect or SetPlayerName() at any time.
It is visible to other players in the room and entirely independent of the UserId.
Authentication Values
AuthenticationValues travels in ConnectOptions.Auth and carries everything the server needs to authenticate the client: the UserId, the provider Type, a provider-specific Parameters string and an optional Data payload.
| Field | Type | Description |
|---|---|---|
UserId |
StringType |
Unique id of the player. Assigned randomly by the server when left empty. |
Type |
CustomAuthenticationType |
The authentication provider. None (default) skips provider authentication. |
Parameters |
StringType |
Provider-specific parameters, typically formatted as a query string. |
Data |
std::variant |
Optional payload: empty, raw bytes (std::vector<uint8_t>), a string or a RealtimeMap. |
Without a provider, setting a UserId is all it takes:
C++
Task<Result<void>> ConnectAsPlayer(RealtimeClient& client)
{
ConnectOptions options;
options.Auth.UserId = PHOTON_STR("player-1234"); // Type stays CustomAuthenticationType::None.
co_return co_await client.Connect(options);
}
C
/* authType 255 is CustomAuthenticationType::None. */
realtime_connect(client, "player-1234", NULL, 255, NULL, NULL);
/* realtime_user_id writes the id the session ended up with, which is the
one to read when the server assigned a random id. */
Authentication Providers
CustomAuthenticationType selects the provider the server validates the login against: Custom, Steam, Facebook, Oculus, PlayStation4, PlayStation5, Xbox, Viveport, NintendoSwitch, Epic and FacebookGaming.
The default None skips provider authentication entirely.
Providers are configured per application in the Photon dashboard, so the client never holds provider secrets.
At connect time the client only supplies the provider-specific Parameters and Data the configured provider expects, such as a session ticket.
For Steam, pass the hex-encoded session ticket in Parameters:
C++
Task<Result<void>> ConnectWithSteam(RealtimeClient& client, RealtimeCore::Common::StringType sessionTicket)
{
ConnectOptions options;
options.Auth.Type = CustomAuthenticationType::Steam;
options.Auth.Parameters = PHOTON_STR("ticket=") + sessionTicket;
co_return co_await client.Connect(options);
}
C
char parameters[256];
snprintf(parameters, sizeof(parameters), "ticket=%s", sessionTicket);
realtime_connect(client,
NULL, /* userId: assigned by Steam authentication */
NULL, /* username */
1, /* authType: CustomAuthenticationType::Steam */
parameters, /* authParameters */
NULL); /* serverAddress */
/* authType is the numeric CustomAuthenticationType, and the value is
truncated to a byte, so an out-of-range number cannot smuggle in an
invalid provider. authParameters is a plain UTF-8 string, so build the
query with whatever string formatting the project already uses. */
Custom Authentication
Type = CustomAuthenticationType::Custom authenticates against your own web service.
Register the service's URL in the dashboard; on every connect the Photon server forwards the client's parameters to it and lets it accept or reject the login.
The Data payload is a std::variant holding either nothing, raw bytes, a string or a RealtimeMap.
A RealtimeMap payload travels as a string-keyed dictionary, so entries with a numeric key are skipped.
Pick whichever shape your authentication service expects: Parameters suits short key-value pairs, Data suits structured or binary content.
This example combines a query-style parameter string with a binary payload:
C++
Task<Result<void>> ConnectWithCustomAuth(RealtimeClient& client)
{
ConnectOptions options;
options.Auth.UserId = PHOTON_STR("player-1234");
options.Auth.Type = CustomAuthenticationType::Custom;
options.Auth.Parameters = PHOTON_STR("token=abc123&build=42");
options.Auth.Data = std::vector<uint8_t>{0x02, 0x48, 0x69};
co_return co_await client.Connect(options);
}
C
realtime_connect(client,
"player-1234", /* userId */
NULL, /* username */
0, /* authType: Custom */
"token=abc123&build=42", /* authParameters */
NULL); /* serverAddress */
/* No Connect-Time Data Payload: AuthenticationValues::Data has no
realtime_connect parameter, so a C client authenticates with Parameters
alone. The realtime_send_custom_auth_data,
realtime_send_custom_auth_string and
realtime_send_custom_auth_properties functions do carry bytes, a string
and a map, but they answer a CustomAuthStep during a multi-step flow
rather than seeding the initial connect. */
Multi-Step Authentication
Some providers need a challenge/response round before the login completes.
Subscribe to OnCustomAuthStep to receive the server's parameters for the next step and answer with SendCustomAuthData(AuthenticationValues).
Answer each step from the callback:
C++
auto subscription = client.OnCustomAuthStep.Subscribe(
[&client](const std::unordered_map<RealtimeCore::Common::StringType, RealtimeCore::Common::StringType>& serverParameters) {
const auto challenge = serverParameters.find(PHOTON_STR("challenge"));
if (challenge == serverParameters.end())
{
return;
}
AuthenticationValues answer;
answer.Type = CustomAuthenticationType::Custom;
answer.Parameters = PHOTON_STR("response=") + challenge->second;
client.SendCustomAuthData(answer);
});
C
/* CustomAuthStep (111) carries a string map:
[int32 count] then, per entry, [string key][string value]. */
if (e->type == RT_CustomAuthStep && e->blobOffset >= 0)
{
rt_reader r = rt_read(blob + e->blobOffset, e->blobLength);
int32_t count = rt_get_i32(&r);
for (int32_t i = 0; i < count; ++i)
{
int32_t keyLen = 0;
const char* key = rt_get_str(&r, &keyLen);
int32_t valLen = 0;
const char* val = rt_get_str(&r, &valLen);
if (keyLen == 9 && memcmp(key, "challenge", 9) == 0)
{
char answer[256];
snprintf(answer, sizeof(answer), "response=%.*s", (int)valLen, val);
realtime_send_custom_auth_string(client, answer);
break;
}
}
}
/* Keys and values in the blob are length-prefixed and not null-terminated,
so compare them by length and memcmp rather than with strcmp.
realtime_send_custom_auth_string sends the reply as a string; use
realtime_send_custom_auth_data for raw bytes or
realtime_send_custom_auth_properties for a map blob. */