Authentication
User Ids and Player Names
The user id 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 publish-user-id room option. If you do not supply one, the server assigns a random user id at connect. That is fine for testing, but stable ids are a prerequisite for friends and rejoin.
The player name is a display name.
Set it through the username connect option at connect (ConnectOptions.Username in C++, username in JavaScript) or at any time (SetPlayerName() in C++, setPlayerName() in JavaScript).
It is visible to other players in the room and entirely independent of the user id.
Authentication Values
The authentication values travel in the connect options (ConnectOptions.Auth in C++, auth in JavaScript) and carry everything the server needs to authenticate the client: the user id, the provider type, a provider-specific parameter string and an optional data payload.
| C++ | C (realtime_connect() parameter) |
JavaScript | Description |
|---|---|---|---|
UserId |
userId |
userId |
Unique id of the player. Assigned randomly by the server when left empty. |
Type |
authType |
type |
The authentication provider. The default, none, skips provider authentication. |
Parameters |
authParameters |
parameters |
Provider-specific parameters, typically formatted as a query string. |
Data |
none | data |
Optional payload: empty, raw bytes, a string or a string-keyed dictionary. See Custom Authentication. |
Without a provider, setting a user id is all it takes:
C++
Task<Result<void, ConnectError>> 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. */
JavaScript
await client.connect({ auth: { userId: 'player-1234' } }); // type stays 'none'
When the server assigned the id, client.userId reads it after the connect.
Authentication Providers
The provider type selects the provider the server validates the login against.
| Provider | C++ (CustomAuthenticationType) |
C (authType) |
JavaScript |
|---|---|---|---|
| None (default) | None |
255 |
'none' |
| Custom | Custom |
0 |
'custom' |
| Steam | Steam |
1 |
'steam' |
Facebook |
2 |
'facebook' |
|
| Oculus | Oculus |
3 |
'oculus' |
| PlayStation 4 | PlayStation4 |
4 |
'playStation4' |
| Xbox | Xbox |
5 |
'xbox' |
| Viveport | Viveport |
10 |
'viveport' |
| Nintendo Switch | NintendoSwitch |
11 |
'nintendoSwitch' |
| PlayStation 5 | PlayStation5 |
12 |
'playStation5' |
| Epic | Epic |
13 |
'epic' |
| Facebook Gaming | FacebookGaming |
15 |
'facebookGaming' |
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 the parameters:
C++
Task<Result<void, ConnectError>> 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. */
JavaScript
// sessionTicket is the hex-encoded session ticket from Steam.
await client.connect({
auth: { type: 'steam', parameters: `ticket=${sessionTicket}` },
});
Custom Authentication
The custom provider type 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.
When the service rejects the login, the connect fails and the disconnect cause reports the failed custom authentication (DisconnectCause::CustomAuthenticationFailed in C++, 'customAuthenticationFailed' in JavaScript).
The data payload holds either nothing, raw bytes, a string or a string-keyed dictionary.
In C++ it is a std::variant of these, and a RealtimeDictionary payload must be created with RealtimeDictionary::Key::String; a dictionary with any other key type sends no data.
In JavaScript it is a Uint8Array, a string or a plain object with string keys.
The C API has no connect-time data payload.
Pick whichever shape your authentication service expects: the parameters suit short key-value pairs, the data payload suits structured or binary content.
This example combines a query-style parameter string with a binary payload:
C++
Task<Result<void, ConnectError>> 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. */
JavaScript
await client.connect({
auth: {
userId: 'player-1234',
type: 'custom',
parameters: 'token=abc123&build=42',
data: new Uint8Array([0x02, 0x48, 0x69]),
},
});
Multi-Step Authentication
Some providers need a challenge/response round before the login completes.
The client receives the server's parameters for the next step as string keys mapped to string values (OnCustomAuthStep in C++, the customAuthStep event in JavaScript, RealtimeEventType_CustomAuthStep in C).
It answers with new authentication values (SendCustomAuthData() in C++, sendCustomAuthData() in JavaScript).
Answer each step from the callback:
C++
auto subscription = client.OnCustomAuthStep.Subscribe(
[&client](const RealtimeDictionary& serverParameters) {
const RealtimeValue* challenge = serverParameters.TryGet(PHOTON_STR("challenge"));
const auto* text = challenge != nullptr ? challenge->TryGet<RealtimeCore::Common::StringType>() : nullptr;
if (text == nullptr)
{
return;
}
AuthenticationValues answer;
answer.Type = CustomAuthenticationType::Custom;
answer.Parameters = PHOTON_STR("response=") + *text;
client.SendCustomAuthData(answer);
});
C
/* The rt_* blob helpers, copied from "Blob Helpers" on the C API page. */
#include "photon_blob.h"
/* CustomAuthStep (111) carries a string map:
[int32 count] then, per entry, [string key][string value]. */
if (e->type == RealtimeEventType_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. */
JavaScript
client.on('customAuthStep', ({ data }) => {
const { challenge } = data;
if (challenge === undefined) {
return;
}
client.sendCustomAuthData({ type: 'custom', parameters: `response=${challenge}` });
});
Last updated on
Back to top