# Authentication

**URL:** https://heroiclabs.com/docs/nakama/concepts/authentication/
**Summary:** Users must authenticate before interacting with the server. This authentication can be done using the user's device, email, a variety of social providers, or your custom solution. Linking authentication methods can be used to support multiple logins and identifying a user across multiple devices.
**Keywords:** authenticate, authentication, auth, login, logout, social, provider, link, unlink, metadata, Apple, Facebook, facebook
**Categories:** nakama, authentication, concepts

---


# Authentication

Nakama requires every client to authenticate before accessing server features. Client applications connect using a [server key](../../getting-started/configuration/#socket.server_key) that identifies and authorizes the application. Individual users then authenticate using one of the supported methods to obtain a session token.

The default server key is `defaultkey`. Change this to a unique value before deploying to production and embed it in your client code.

<!-- more -->

{{< code type="client" >}}

```javascript
var useSSL = false;
var client = new nakamajs.Client("defaultkey", "127.0.0.1", 7350, useSSL);
```

{{< / code >}}

{{< code type="client" >}}

```csharp
// Use "https" scheme if you've setup SSL.
var client = new Client("http", "127.0.0.1", 7350, "defaultkey");
```

{{< / code >}}

{{< code type="client" >}}

```cpp
NClientParameters parameters;
parameters.serverKey = "defaultkey";
parameters.host = "127.0.0.1";
parameters.port = DEFAULT_PORT;
NClientPtr client = createDefaultClient(parameters);
```

{{< / code >}}

{{< code type="client" >}}

```java
Client client = new DefaultClient("defaultkey", "127.0.0.1", 7349, false)
// or same as above.
Client client = DefaultClient.defaults("defaultkey");
```

{{< / code >}}

{{< code type="client" framework="godot3" >}}

```gdscript
onready var client := Nakama.create_client("defaultkey", "127.0.0.1", 7350, "http")
```

{{< / code >}}

{{< code type="client" framework="godot4" >}}

```gdscript
@onready var client := Nakama.create_client("defaultkey", "127.0.0.1", 7350, "http")
```

{{< / code >}}

{{< code type="client" framework="defold">}}

```lua
local defold = require "nakama.engine.defold"

local config = {}
config.host = 127.0.0.1
config.port = 7350
config.use_ssl = false
config.username = "defaultkey"
config.engine = defold

local client = nakama.create_client(config)
```

{{< / code >}}

{{< code type="client" >}}

```swift
let client = GrpcClient(serverKey: "defaultkey", trace: true)
```

{{< / code >}}

{{< code type="client" >}}

```dart
final client = getNakamaClient(
  host: '127.0.0.1',
  ssl: false,
  serverKey: 'defaultkey',
);
```

{{< / code >}}

Each user account can have multiple authentication methods [linked](#link-or-unlink) to it, allowing the same user to sign in across different devices or platforms.

## Authentication methods

Nakama supports a range of authentication methods: device ID, email, several social providers, and custom identifiers. Each method produces a session token that gives the client access to the server.

By default, authenticating with an identifier that doesn't yet exist in the system will automatically create a new user account. To authenticate without creating an account, set `create` to `false`.

For full SDK examples, see the [client library guides](../../client-libraries/).

### Device

A device identifier can be used as a way to unobtrusively register a user with the server. This offers a frictionless user experience but can be unreliable because device identifiers can sometimes change in device updates.

You can choose a custom username when creating the account. To do this, set `username` to a custom name. If you want to only authenticate without implicitly creating a user account, set `create` to false.

A device identifier must contain alphanumeric characters with dashes and be between 10 and 128 bytes.

{{< code type="client" >}}

```bash
curl "http://127.0.0.1:7350/v2/account/authenticate/device?create=true&username=mycustomusername" \
  --user 'defaultkey:' \
  --data '{"id":"uniqueidentifier"}'
```

{{< / code >}}

{{< code type="client" >}}

```shell
POST /v2/account/authenticate/device?create=true&username=mycustomusername
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Basic base64(ServerKey:)
{
  "id": "uniqueidentifier"
}
```

{{< / code >}}

{{< code type="client" >}}

```javascript
// This import is only required with React Native
var deviceInfo = require("react-native-device-info");
var deviceId = null;

try {
  const value = await AsyncStorage.getItem("@MyApp:deviceKey");

  if (value !== null) {
    deviceId = value;
  } else {
    deviceId = deviceInfo.getUniqueID();

    AsyncStorage.setItem("@MyApp:deviceKey", deviceId).catch(function (error) {
      console.log("An error occurred: %o", error);
    });
  }
} catch (error) {
  console.log("An error occurred: %o", error);
}

var create = true;
const session = await client.authenticateDevice(
  deviceId,
  create,
  "mycustomusername",
);
console.info("Successfully authenticated:", session);
```

{{< / code >}}

{{< code type="client" >}}

```csharp
// Should use a platform API to obtain a device identifier.
var deviceId = System.Guid.NewGuid().ToString();
var session = await client.AuthenticateDeviceAsync(deviceId);
System.Console.WriteLine("New user: {0}, {1}", session.Created, session);
```

{{< / code >}}

{{< code type="client" >}}

```cpp
auto loginFailedCallback = [](const NError& error)
{
};

auto loginSucceededCallback = [](NSessionPtr session)
{
    cout << "Successfully authenticated" << endl;
};

std::string deviceId = "unique device id";

client->authenticateDevice(deviceId, opt::nullopt, opt::nullopt, {}, loginSucceededCallback, loginFailedCallback);
```

{{< / code >}}

{{< code type="client" >}}

```java
String id = UUID.randomUUID().toString();
Session session = client.authenticateDevice(id).get();
System.out.format("Session: %s ", session.getAuthToken());
```

{{< / code >}}

{{< code type="client" framework="godot3" >}}

```gdscript
# Unique ID is not supported by Godot in HTML5, use a different way to generate an id, or a different authentication option.
var deviceid = OS.get_unique_id()
var session : NakamaSession = yield(client.authenticate_device_async(deviceid), "completed")

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="godot4" >}}

```gdscript
# Unique ID is not supported by Godot in HTML5, use a different way to generate an id, or a different authentication option.
var deviceid = OS.get_unique_id()
var session : NakamaSession = await client.authenticate_device_async(deviceid)

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="defold">}}

```lua
-- login using the token and create an account if the user
-- doesn't already exist
local result = client.authenticate_device(defold,uuid(), nil, true, "mycustomusername")

if not result.token then
  print("Unable to login")
end

-- store the token and use it when communicating with the server
client.set_bearer_token(result.token)
```

{{< / code >}}

{{< code type="client" >}}

```swift
guard let deviceId = await UIDevice.current.identifierForVendor?.uuidString else { return }

do {
    let session = try await client.authenticateDevice(id: deviceId, create: create, username: "mycustomusername")
    print("Successfully authenticated:", session)
} catch {
    debugPrint(error)
}
```

{{< / code >}}

{{< code type="client" >}}

```dart
// You can obtain device id for multiplatforms using a package like `device_info_plus`
final session = await client.authenticateDevice(deviceId: '<obtained_device_id>');
```

{{< / code >}}

In games it is often a better option to use [Google](#google) or [Game Center](#game-center) to unobtrusively register the user.

### Email

Users can be registered with an email and password. The password is hashed before it's stored in the database server and cannot be read or "recovered" by administrators. This protects a user's privacy.

You can choose a custom username when creating the account. To do this, set `username` to a custom name. If you want to only authenticate without implicitly creating a user account, set `create` to false.

An email address must be valid as defined by RFC-5322 and passwords must be at least 8 characters.

Once an account has been created with email + password (and optionally a username), you may also authenticate with username + password using the same email authentication endpoint (or SDK function). In this case, pass the email argument as an empty string ("").

{{< code type="client" >}}

```bash
curl "http://127.0.0.1:7350/v2/account/authenticate/email?create=true&username=mycustomusername" \
  --user 'defaultkey:' \
  --data '{"email":"email@example.com", "password": "3bc8f72e95a9"}'
```

{{< / code >}}

{{< code type="client" >}}

```shell
POST /v2/account/authenticate/email?create=true&username=mycustomusername
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Basic base64(ServerKey:)
{
  "email": "email@example.com",
  "password": "3bc8f72e95a9"
}
```

{{< / code >}}

{{< code type="client" >}}

```javascript
const email = "email@example.com";
const password = "3bc8f72e95a9";
const create = true;
const session = await client.authenticateEmail(
  email,
  password,
  create,
  "mycustomusername",
);
console.info("Successfully authenticated:", session);

// Alternatively, authenticate with username + password only:
const session2 = await client.authenticateEmail(
  "",
  password,
  false,
  "mycustomusername",
);
```

{{< / code >}}

{{< code type="client" >}}

```csharp
const string email = "email@example.com";
const string password = "3bc8f72e95a9";
var session = await client.AuthenticateEmailAsync(email, password);
System.Console.WriteLine("New user: {0}, {1}", session.Created, session);

// Alternatively, authenticate with username + password only:
var session2 = await client.AuthenticateEmailAsync("", password, "mycustomusername", false);
```

{{< / code >}}

{{< code type="client" >}}

```cpp
auto successCallback = [](NSessionPtr session)
{
    std::cout << "Authenticated successfully. User ID: " << session->getUserId() << std::endl;
};

auto errorCallback = [](const NError& error)
{
};

string email = "email@example.com";
string password = "3bc8f72e95a9";
string username = "mycustomusername";
bool create = true;
client->authenticateEmail(email, password, username, create, {}, successCallback, errorCallback);


// Alternatively, authenticate with username + password only:
client->authenticateEmail("", password, username, false, {}, successCallback, errorCallback);
```

{{< / code >}}

{{< code type="client" >}}

```java
String email = "email@example.com";
String password = "3bc8f72e95a9";
Session session = client.authenticateEmail(email, password).get();
System.out.format("Session: %s ", session.getAuthToken());

// Alternatively, authenticate with username + password only:
Session session2 = client.authenticateEmail(
  "",                    // email empty
  password,
  "mycustomusername",    // username
  false                  // create = false (authenticate only)
).get();
```

{{< / code >}}

{{< code type="client" framework="godot3" >}}

```gdscript
var email = "email@example.com"
var password = "3bc8f72e95a9"
var session : NakamaSession = yield(client.authenticate_email_async(email, password, "mycustomusername", true), "completed")

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)

# Alternatively, authenticate with username + password only:
var session2 : NakamaSession = yield(
  client.authenticate_email_async("", password, "mycustomusername", false),
  "completed"
)
```

{{< / code >}}

{{< code type="client" framework="godot4" >}}

```gdscript
var email = "email@example.com"
var password = "3bc8f72e95a9"
var session : NakamaSession = await client.authenticate_email_async(email, password, "mycustomusername", true)

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)

# Alternatively, authenticate with username + password only:
var session2 : NakamaSession = await client.authenticate_email_async(
  "",                     # email empty
  password,
  "mycustomusername",
  false                   # create = false
)
```

{{< / code >}}

{{< code type="client" framework="defold">}}

```lua
local result = client.authenticate_email("email@example.com", "3bc8f72e95a9", nil, true, "mycustomusername")

if not result.token then
  print("Unable to login")
end

-- store the token and use it when communicating with the server
client.set_bearer_token(result.token)

-- Alternatively, authenticate with username + password only:
local result2 = client.authenticate_email("", "3bc8f72e95a9", nil, false, "mycustomusername")

```

{{< / code >}}

{{< code type="client" >}}

```swift
do {
    let session = try await client.authenticateEmail(email: "email", password: "password", create: true, username: "mycustomusername")
    print("Successfully authenticated:", session)
} catch {
    debugPrint(error)
}

// Alternatively, authenticate with username + password only:
do {
    let session2 = try await client.authenticateEmail(
        email: "",
        password: "password",
        create: false,             // authenticate only
        username: "mycustomusername"
    )
    print("Successfully authenticated:", session2)
} catch {
    debugPrint(error)
}

```

{{< / code >}}

{{< code type="client" >}}

```dart
try {
  session = await client.authenticateEmail(
    email: 'email',
    password: 'password',
    create: true,
    username: 'mycustomusername',
  );
  print('Successfully authenticated: $session');
} catch (error) {
  print(error);
}

// Alternatively, authenticate with username + password only:
try {
  final session2 = await client.authenticateEmail(
    email: '',
    password: 'password',
    create: false,              // authenticate only
    username: 'mycustomusername',
  );
  print('Successfully authenticated: $session2');
} catch (error) {
  print(error);
}
```

{{< / code >}}

### Console

Nakama supports console authentication for all major platforms, including Nintendo Switch, Xbox One and Series X|S, and PlayStation 4/5.

{{< note important "Requesting Access">}}
Console authentication is only available to developers verified by the respective console platform providers (Sony, Microsoft, Nintendo). To request access, please [contact us](mailto:support@heroiclabs.com).
{{< /note >}}

The console authentication flow is similar to the [device](#device) authentication flow, but requires a console identifier and a console user identifier.

Note that the examples below are only meant to be illustrative. Complete documentation can only be provided following your verification by the console platform provider(s).

{{< code type="client" >}}

```csharp
// Xbox authentication
var session = await client.AuthenticateCustomAsync("xbox|<xbox_code>");

// PlayStation authentication
var session = await client.AuthenticateCustomAsync("psn|<<psn_code>>");

// Nintendo authentication
var session = await client.AuthenticateCustomAsync("nintendo|<<nintendo_code>>");
```

{{< / code >}}

{{< code type="client" >}}

```cpp
// Xbox authentication
client->authenticateCustom("xbox|<xbox_code>", {}, successCallback, errorCallback);

// PlayStation authentication
client->authenticateCustom("psn|<<psn_code>>", {}, successCallback, errorCallback);

// Nintendo authentication
client->authenticateCustom("nintendo|<<nintendo_code>>", {}, successCallback, errorCallback);
```

{{< / code >}}

{{< missing type="client" lang="lua" framework="defold" />}}
{{< missing type="client" lang="bash" />}}
{{< missing type="client" lang="shell" />}}
{{< missing type="client" lang="javascript" />}}
{{< missing type="client" lang="java" />}}
{{< missing type="client" lang="gdscript-godot3" />}}
{{< missing type="client" lang="gdscript-godot4" />}}

### Social providers

The server supports a lot of different social services with register and login. With each provider the user account will be fetched from the social service and used to setup the user. In some cases a user's [friends](../friends/) will also be fetched and added to their friends list.

To register or login as a user with any of the providers an OAuth or access token must be obtained from that social service.

#### Apple

Follow the Apple Developer documentation for integrating [Sign in with Apple](https://developer.apple.com/sign-in-with-apple/get-started/) in your applications.

You can choose a custom username when creating the account. To do this, set `username` to a custom name. If you want to only authenticate without implicitly creating a user account, set `create` to false.

{{< code type="client" >}}

```bash
curl "http://127.0.0.1:7350/v2/account/authenticate/apple?create=true&username=mycustomusername" \
  --user 'defaultkey:' \
  --data '{"token":"..."}'
```

{{< / code >}}

{{< code type="client" >}}

```shell
POST /v2/account/authenticate/apple?create=true&username=mycustomusername
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Basic base64(ServerKey:)
{
  "token": "...",
}
```

{{< / code >}}

{{< code type="client" >}}

```javascript
const token = "...";
const create = true;
const username = "...";
const session = await client.authenticateApple(token, create, username);
console.info("Successfully authenticated: %o", session);
```

{{< / code >}}

{{< code type="client" >}}

```csharp
var username = "...";
var token = "...";

var session = await client.AuthenticateAppleAsync(token, username);
System.Console.WriteLine("New user: {0}, {1}", session.Created, session);
```

{{< / code >}}

{{< code type="client" >}}

```cpp
auto successCallback = [](NSessionPtr session)
{
    std::cout << "Authenticated successfully. User ID: " << session->getUserId() << std::endl;
};

auto errorCallback = [](const NError& error)
{
};

std::string username = "...";
std::string token = "...";

client->authenticateApple(token, username);
```

{{< / code >}}

{{< code type="client" framework="godot3" >}}

```gdscript
var username = "..."
var token = "..."

var session : NakamaSession = yield(client.authenticate_apple_async(token, username), "completed")

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="godot4" >}}

```gdscript
var username = "..."
var token = "..."

var session : NakamaSession = await client.authenticate_apple_async(token, username)

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{{< code type="client" >}}

```java
String token = "...";
Session session = client.authenticateApple(token).get();
System.out.format("Session %s", session.getAuthToken());
```

{{< / code >}}

{{< code type="client" framework="defold">}}

```lua
-- Use https://defold.com/assets/siwa/
local token = "..."
local username = "..."
local result = client.authenticate_apple(token, username)

if not result.token then
  print("Unable to login")
end

-- store the token and use it when communicating with the server
client.set_bearer_token(result.token)
```

{{< / code >}}

{{< code type="client" >}}

```swift
let oauthToken = "<token>"
var username = "..."

do {
    let session = try await client.authenticateApple(oauthToken: oauthToken, username: "mycustomusername")
    print("Successfully authenticated:", session)
} catch {
    debugPrint(error)
}
```

{{< / code >}}

{{< code type="client" >}}

```dart
final oauthToken = '<token>';
final username = '...';

final session = await client.authenticateApple(
  token: oauthToken,
  username: 'mycustomusername',
);
```

{{< / code >}}

##### Game Center

Game Center provides built-in authentication on Apple devices without requiring user interaction. To authenticate, pass the player credentials provided by Apple's GameKit framework.

You can choose a custom username when creating the account. To do this, set `username` to a custom name. If you want to only authenticate without implicitly creating a user account, set `create` to false.

{{< code type="client" >}}

```java
String playerId = "...";
String bundleId = "...";
Long timestampSeconds = <...>;
String salt = "...";
String signature = "...";
String publicKeyUrl = "...";
Session session = client.authenticateGameCenter(playerId, bundleId, timestampSeconds, salt, signature, publicKeyUrl).get();
System.out.format("Session %s", session.getAuthToken());
```

{{< / code >}}

{{< code type="client" >}}

```bash
curl "http://127.0.0.1:7350/v2/account/authenticate/gamecenter?create=true&username=mycustomusername" \
  --user 'defaultkey:' \
  --data '{"player_id":"...", "bundle_id":"...", "timestamp_seconds":0, "salt":"...", "public_key_url":"..."}'
```

{{< / code >}}

{{< code type="client" >}}

```shell
POST /v2/account/authenticate/gamecenter?create=true&username=mycustomusername
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Basic base64(ServerKey:)
{
  "player_id": "...",
  "bundle_id": "...",
  "timestamp_seconds": 0,
  "salt": "...",
  "signature": "...",
  "public_key_url": "..."
}
```

{{< / code >}}

{{< code type="client" >}}

```csharp
var bundleId = "...";
var playerId = "...";
var publicKeyUrl = "...";
var salt = "...";
var signature = "...";
var timestamp = "...";
var session = await client.AuthenticateGameCenterAsync(bundleId, playerId,
    publicKeyUrl, salt, signature, timestamp);
System.Console.WriteLine("New user: {0}, {1}", session.Created, session);
```

{{< / code >}}

{{< code type="client" >}}

```javascript
const bundleId = "...";
const playerId = "...";
const publicKeyUrl = "...";
const salt = "...";
const signature = "...";
const timestamp = "...";
const session = await client.authenticateGameCenter(
  bundleId,
  playerId,
  publicKeyUrl,
  salt,
  signature,
  timestamp,
);
console.info("Successfully authenticated: %o", session);
```

{{< / code >}}

{{< code type="client" >}}

```cpp
auto successCallback = [](NSessionPtr session)
{
    std::cout << "Authenticated successfully. User ID: " << session->getUserId() << std::endl;
};

auto errorCallback = [](const NError& error)
{
};

std::string playerId = "...";
std::string    bundleId = "...";
NTimestamp timestampSeconds = "...";
std::string salt = "...";
std::string signature = "...";
std::string publicKeyUrl = "...";

client->authenticateGameCenter(playerId, bundleId, timestampSeconds, salt, signature, publicKeyUrl, "mycustomusername", true, {}, successCallback, errorCallback);
```

{{< / code >}}

{{< code type="client" framework="godot3" >}}

```gdscript
var bundle_id = "..."
var player_id = "..."
var public_key_url = "..."
var salt = "..."
var signature = "..."
var timestamp = "..."
var session : NakamaSession = yield(client.authenticate_game_center_async(bundle_id, player_id, public_key_url, salt, signature, timestamp), "completed")

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="godot4" >}}

```gdscript
var bundle_id = "..."
var player_id = "..."
var public_key_url = "..."
var salt = "..."
var signature = "..."
var timestamp = "..."
var session : NakamaSession = await client.authenticate_game_center_async(bundle_id, player_id, public_key_url, salt, signature, timestamp)

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="defold">}}

```lua
-- Use https://defold.com/assets/gamekit/
local bundle_id = "..."
local player_id = "..."
local public_key_url = "..."
local salt = "..."
local signature = "..."
local timestamp_seconds = 0
local result = client.authenticate_game_center(bundle_id, player_id, public_key_url, salt, signature, timestamp_seconds, nil, true, "mycustomusername")

if not result.token then
  print("Unable to login")
end

-- store the token and use it when communicating with the server
client.set_bearer_token(result.token)
```

{{< / code >}}

{{< code type="client" >}}

```swift
let playerId = "<playerId>"
var bundleId = "<bundleId>"
var timestampSeconds = <timestampSeconds>
var salt = "<salt>"
var signature = "<signature>"
var publicKeyUrl = "<publicKeyUrl>"
var create = true

do {
    let session = try await client.authenticateGameCenter(playerId: playerId, bundleId: bundleId, timestampSeconds: timestampSeconds, salt: salt, signature: signature, publicKeyUrl: publicKeyUrl, create: create, username: "mycustomusername")
    print("Successfully authenticated:", session)
} catch {
    debugPrint(error)
}
```

{{< / code >}}

{{< code type="client" >}}

```dart
final playerId = '<playerId>';
final bundleId = '<bundleId>';
final timestampSeconds = DateTime.now().millisecondsSinceEpoch ~/ 1000;
final salt = '<salt>';
final signature = '<signature>';
final publicKeyUrl = '<publicKeyUrl>';
final create = true;

final session = await client.authenticateGameCenter(
  playerId: playerId,
  bundleId: bundleId,
  timestampSeconds: timestampSeconds,
  salt: salt,
  signature: signature,
  publicKeyUrl: publicKeyUrl,
  create: create,
  username: 'mycustomusername',
);
```

{{< / code >}}

#### Facebook

With Facebook you'll need to add the Facebook SDK to your project which can be <a href="https://developers.facebook.com/docs/" target="\_blank">downloaded online</a>. Follow their guides on how to integrate the code. With a mobile project you'll also need to complete instructions on how to configure iOS and Android.

You can choose a custom username when creating the account. To do this, set `username` to a custom name. If you want to only authenticate without implicitly creating a user account, set `create` to false.

When authenticating via Facebook, the following profile fields are fetched and stored in the user account:

- ID
- Name
- Email
- Photo

Additionally, you can optionally import Facebook friends into Nakama's [friend graph](../friends/) when authenticating. To do this, set `import` to true.

{{< code type="client" >}}

```bash
curl "http://127.0.0.1:7350/v2/account/authenticate/facebook?create=true&username=mycustomusername&import=true" \
    --user 'defaultkey:' \
    --data '{"token":"valid-oauth-token"}' # Supports the use of both OAuth and Facebook Limited Login (JWT) tokens.
```

{{< / code >}}

{{< code type="client" >}}

```shell
POST /v2/account/authenticate/facebook?create=true&username=mycustomusername&import=true
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Basic base64(ServerKey:)
{
  "token": "...",
}
```

{{< / code >}}

{{< code type="client" >}}

```javascript
const oauthToken = "...";
const import = true;
const session = await client.authenticateFacebook(oauthToken, true, "mycustomusername", import);
console.log("Successfully authenticated:", session);
```

{{< / code >}}

{{< code type="client" >}}

```csharp
const string oauthToken = "...";
var session = await client.AuthenticateFacebookAsync(oauthToken);
System.Console.WriteLine("New user: {0}, {1}", session.Created, session);
```

{{< / code >}}

{{< code type="client" >}}

```cpp
auto loginFailedCallback = [](const NError& error)
{
};

auto loginSucceededCallback = [](NSessionPtr session)
{
    cout << "Authenticated successfully. User ID: " << session->getUserId() << endl;
};

std::string oauthToken = "...";
bool importFriends = true;
client->authenticateFacebook(oauthToken, "mycustomusername", true, importFriends, {}, loginSucceededCallback, loginFailedCallback);
```

{{< / code >}}

{{< code type="client" >}}

```java
String oauthToken = "...";
Session session = client.authenticateFacebook(oauthToken).get();
System.out.format("Session %s", session.getAuthToken());
```

{{< / code >}}

{{< code type="client" framework="godot3" >}}

```gdscript
var oauth_token = "..."
var session : NakamaSession = yield(client.authenticate_facebook_async(oauth_token), "completed")

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="godot4" >}}

```gdscript
var oauth_token = "..."
var session : NakamaSession = await client.authenticate_facebook_async(oauth_token)

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="defold">}}

```lua
-- Use the official Defold Facebook integration (www.defold.com/extension-facebook)
local permissions = { "public_profile" }
-- login using read permissions
-- there is no need to specify a publishing audience when requesting read permissions
facebook.login_with_permissions(permissions, facebook.AUDIENCE_NONE, function(self, data)
  local result = client.authenticate_facebook(facebook.access_token(), nil, true, "mycustomusername")

  if not result.token then
    print("Unable to login")
    return
  end
  -- store the token and use it when communicating with the server
  client.set_bearer_token(result.token)
end)
```

{{< / code >}}

{{< code type="client" >}}

```swift
let oauthToken = "<token>"
var importFriends = true

do {
    let session = try await client.authenticateFacebook(accessToken: oauthToken, create: true, username: "mycustomusername", importFriends: importFriends)
    print("Successfully authenticated:", session)
} catch {
    debugPrint(error)
}
```

{{< / code >}}

{{< code type="client" >}}

```dart
final oauthToken = '<token>';
final importFriends = true;

final session = await client.authenticateFacebook(
  token: oauthToken,
  create: true,
  username: 'mycustomusername',
  importFriends: importFriends,
);
```

{{< / code >}}

{{< note "important" "Facebook Access Token Validator" >}}
The Facebook SDK supports various types of authentication tokens, Nakama Authentication supports [User Tokens](https://developers.facebook.com/docs/facebook-login/guides/access-tokens#usertokens) (opaque) and [Limited Login Tokens](https://developers.facebook.com/docs/facebook-login/limited-login/) (OpenID Connect JWT).
You should be aware that friend import only works with the former tokens, as Nakama needs to access the FB Graph API. Friend data is available only for users that have authorized your App on FB (both the authenticating user and their friends).
In the case that Access Tokens are used, this [Access Token Validator](https://developers.facebook.com/tools/debug/accesstoken/) may be useful to debug any issues you encounter while integrating with the Nakama Authentication APIs.
{{< / note >}}

#### Facebook Instant

Ensure that you've [configured](../../getting-started/configuration/#facebook-instant-game) your FB Instant App secret for Nakama and initialized the Facebook Instant Games SDK using `FBInstant.initializeAsync()`.

{{< missing type="client" lang="csharp" />}}
{{< missing type="client" lang="cpp" />}}

{{< code type="client" >}}

```bash
curl "http://127.0.0.1:7350/v2/account/authenticate/facebookinstantgame?create=true&username=mycustomusername" \
    --user 'defaultkey:' \
    --data '{"token":"valid-oauth-token"}' # Supports the use of both OAuth and Facebook Limited Login (JWT) tokens.
```

{{< / code >}}

{{< code type="client" >}}

```javascript
const result = await FBInstant.player.getSignedPlayerInfoAsync();
var session = await client.authenticateFacebookInstantGame(
  result.getSignature(),
);
console.info("Successfully authenticated: %o", session);
```

{{< / code >}}

{{< code type="client" >}}

```java
String accessToken = "...";
Session session = client.authenticateFacebookInstant(accessToken).get();
System.out.format("Session %s", session.getAuthToken());
```

{{< / code >}}

{{< code type="client" framework="godot3" >}}

```gdscript
var signed_player_info = "..."
var session : NakamaSession = yield(client.authenticate_facebook_instant_game_async(signed_player_info), "completed")

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="godot4" >}}

```gdscript
var signed_player_info = "..."
var session : NakamaSession = await client.authenticate_facebook_instant_game_async(signed_player_info)

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="defold">}}

```lua
-- Use the official Defold Facebook Instant Games integration (www.defold.com/extension-fbinstant)
fbinstant.get_signed_player_info("developer payload", function(self, signature)
  local result = client.authenticate_facebook_instant_game(signature, nil, true, "mycustomusername")

  if not result.token then
    print("Unable to login")
    return
  end
  -- store the token and use it when communicating with the server
  client.set_bearer_token(result.token)
end)
```

{{< / code >}}

{{< code type="client" >}}

```swift
let token = "<token>"

do {
    let session = try await client.authenticateFacebookInstantGame(token: token, create: true, username: "mycustomusername")
    print("Successfully authenticated:", session)
} catch {
    debugPrint(error)
}
```

{{< / code >}}

{{< code type="client" >}}

```dart
final token = '<token>';

final session = await client.authenticateFacebookInstantGame(
  signedPlayerInfo: token,
  create: true,
  username: 'mycustomusername',
);
```

{{< / code >}}

#### Google

Nakama supports two Google authentication flows through the same endpoint (`/v2/account/authenticate/google`) and SDK method (`AuthenticateGoogleAsync`). The credential type you pass determines which flow is used:

- **Google Sign-In**: Pass the ID token (JWT) from the [Google Sign-In SDK](https://developers.google.com/identity/sign-in/).
- **Play Game Services (PGS) v2**: Pass the server-side auth code from the Play Games SDK. Requires additional Nakama server configuration (see below)

You can choose a custom username when creating the account. To do this, set `username` to a custom name. If you want to only authenticate without implicitly creating a user account, set `create` to false.

#### Google Sign-In

Pass the ID token returned by the Google Sign-In SDK as the `token` parameter:

{{< code type="client" >}}

```bash
curl "http://127.0.0.1:7350/v2/account/authenticate/google?create=true&username=mycustomusername" \
  --user 'defaultkey:' \
  --data '{"token":"valid-oauth-token"}'
```

{{< / code >}}

{{< code type="client" >}}

```shell
POST /v2/account/authenticate/google?create=true&username=mycustomusername
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Basic base64(ServerKey:)
{
  "token": "...",
}
```

{{< / code >}}

{{< code type="client" >}}

```javascript
const playerIdToken = "...";
const create = true;
const session = await client.authenticateGoogle(
  oauthToken,
  create,
  "mycustomusername",
);
console.info("Successfully authenticated: %o", session);
```

{{< / code >}}

{{< code type="client" >}}

```csharp
const string playerIdToken = "...";
var session = await client.AuthenticateGoogleAsync(playerIdToken);
System.Console.WriteLine("New user: {0}, {1}", session.Created, session);
```

{{< / code >}}

{{< code type="client" >}}

```cpp
auto successCallback = [](NSessionPtr session)
{
    std::cout << "Authenticated successfully. User ID: " << session->getUserId() << std::endl;
};

auto errorCallback = [](const NError& error)
{
};

string oauthToken = "...";
client->authenticateGoogle(oauthToken, "mycustomusername", true, {}, successCallback, errorCallback);
```

{{< / code >}}

{{< code type="client" >}}

```java
String accessToken = "...";
Session session = client.authenticateGoogle(accessToken).get();
System.out.format("Session %s", session.getAuthToken());
```

{{< / code >}}

{{< code type="client" framework="godot3" >}}

```gdscript
var oauth_token = "..."
var session : NakamaSession = yield(client.authenticate_google_async(oauth_token), "completed")

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="godot4" >}}

```gdscript
var oauth_token = "..."
var session : NakamaSession = await client.authenticate_google_async(oauth_token)

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="defold">}}

```lua
local result = client.authenticate_google(oauth_token, nil, true, "mycustomusername")

if not result.token then
  print("Unable to login")
end

-- store the token and use it when communicating with the server
client.set_bearer_token(result.token)
```

{{< / code >}}

#### Play Game Services (PGS)

PGS v1 is deprecated; use PGS v2. PGS v2 uses server-side access: the client obtains a server-side auth code from the Play Games SDK, which Nakama exchanges for credentials on the server. Pass this server-side auth code as the `token` parameter. The SDK call is otherwise identical to Google Sign-In above.

Because the exchange happens server-side, Nakama must be configured with credentials that authorize it to call the Play Games API. Obtain the credentials JSON from your [Play Games server-side setup](https://developers.google.com/games/services/android/offline-access) — these must include read scopes for the [Players:get endpoint](https://developers.google.com/games/services/web/api/players/get#auth). These credentials are separate from any service account used for [IAP validation](../../concepts/iap-validation/google/).

{{< note "important" "Nakama configuration required" >}}
Set [google_auth.credentials_json](../../server-framework/server-configuration/#google-auth) in your Nakama server configuration before using PGS v2 authentication.
{{< / note >}}

#### Steam

Steam requires you to configure the server before you can register a user. Have a look at the [configuration](../../getting-started/configuration/#steam) section for what settings you need for the server.

You can choose a custom username when creating the account. To do this, set `username` to a custom name. If you want to only authenticate without implicitly creating a user account, set `create` to false.

You can optionally import Steam friends into Nakama's [friend graph](../friends/) when authenticating. To do this, set `import` to true.

{{< code type="client" >}}

```bash
curl "http://127.0.0.1:7350/v2/account/authenticate/steam?create=true&username=mycustomusername&import=true" \
  --user 'defaultkey:' \
  --data '{"token":"valid-steam-token"}'
```

{{< / code >}}

{{< code type="client" >}}

```shell
POST /v2/account/authenticate/steam?create=true&username=mycustomusername&import=true
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Basic base64(ServerKey:)
{
  "token": "...",
}
```

{{< / code >}}

{{< code type="client" >}}

```javascript
const token = "...";
const create = true;
const username = "...";
const sync = true;
const session = await client.authenticateSteam(token, create, username, sync);
console.info("Successfully authenticated: %o", session);
```

{{< / code >}}

{{< code type="client" >}}

```csharp
const string token = "...";
const bool importFriends = true;
var session = await client.AuthenticateSteamAsync(token, importFriends);
System.Console.WriteLine("New user: {0}, {1}", session.Created, session);
```

{{< / code >}}

{{< code type="client" >}}

```cpp
auto successCallback = [](NSessionPtr session)
{
    std::cout << "Authenticated successfully. User ID: " << session->getUserId() << std::endl;
};

auto errorCallback = [](const NError& error)
{
};

string token = "...";
string username = "mycustomusername";
bool create = true;
bool importFriends = true;
client->authenticateSteam(token, username, create, importFriends, {}, successCallback, errorCallback);
```

{{< / code >}}

{{< code type="client" >}}

```java
String token = "...";
Bool importFriends = true;
Session session = client.authenticateSteam(token, importFriends).get();
System.out.format("Session %s", session.getAuthToken());
```

{{< / code >}}

{{< code type="client" framework="godot3" >}}

```gdscript
var steam_token = "..."
var import_friends = true
var session : NakamaSession = yield(client.authenticate_steam_async(steam_token, import_friends), "completed")

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="godot4" >}}

```gdscript
var steam_token = "..."
var import_friends = true
var session : NakamaSession = await client.authenticate_steam_async(steam_token, import_friends)

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="defold">}}

```lua
-- Use https://defold.com/assets/steamworks/
local result = client.authenticate_steam(steam_token, nil, true, "mycustomusername")

if not result.token then
  print("Unable to login")
end

-- store the token and use it when communicating with the server
client.set_bearer_token(result.token)
```

{{< / code >}}

{{< code type="client" >}}

```swift
let token = "<token>"
var create = true

do {
    let session = try await client.authenticateSteam(accessToken: token, create: create, username: "mycustomusername")
    print("Successfully authenticated:", session)
} catch {
    debugPrint(error)
}
```

{{< / code >}}

{{< code type="client" >}}

```dart
final token = '<token>';
final create = true;

final session = await client.authenticateSteam(
  token: token,
  create: create,
  username: 'mycustomusername',
);
```

{{< / code >}}

### Custom

A custom identifier can be used in a similar way to a device identifier to login or register a user. This option should be used if you have an external or custom user identity service which you want to use. For example EA's Origin service handles accounts which have their own user IDs.

A custom identifier must contain alphanumeric characters with dashes and be between 6 and 128 bytes.

You can choose a custom username when creating the account. To do this, set `username` to a custom name. If you want to only authenticate without implicitly creating a user account, set `create` to false.

{{< code type="client" >}}

```bash
curl "http://127.0.0.1:7350/v2/account/authenticate/custom?create=true&username=mycustomusername" \
  --user 'defaultkey:' \
  --data '{"id":"some-custom-id"}'
```

{{< / code >}}

{{< code type="client" >}}

```shell
POST /v2/account/authenticate/custom?create=true&username=mycustomusername
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Basic base64(ServerKey:)
{
  "id": "some-custom-id",
}
```

{{< / code >}}

{{< code type="client" >}}

```javascript
const customId = "some-custom-id";
const create = true;
const session = await client.authenticateCustom(
  customId,
  create,
  "mycustomusername",
);
console.info("Successfully authenticated:", session);
```

{{< / code >}}

{{< code type="client" >}}

```csharp
const string customId = "some-custom-id";
var session = await client.AuthenticateCustomAsync(customId);
System.Console.WriteLine("New user: {0}, {1}", session.Created, session);
```

{{< / code >}}

{{< code type="client" >}}

```cpp
auto successCallback = [](NSessionPtr session)
{
    std::cout << "Authenticated successfully. User ID: " << session->getUserId() << std::endl;
};

auto errorCallback = [](const NError& error)
{
};

string id = "some-custom-id";
string username = "mycustomusername";
bool create = true;
client->authenticateCustom(id, username, create, {}, successCallback, errorCallback);
```

{{< / code >}}

{{< code type="client" >}}

```java
String customId = "some-custom-id";
Session session = client.authenticateCustom(customId).get();
System.out.format("Session %s", session.getAuthToken());
```

{{< / code >}}

{{< code type="client" framework="godot3" >}}

```gdscript
var custom_id = "some-custom-id"
var session : NakamaSession = yield(client.authenticate_custom_async(custom_id), "completed")

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="godot4" >}}

```gdscript
var custom_id = "some-custom-id"
var session : NakamaSession = await client.authenticate_custom_async(custom_id)

if session.is_exception():
    print("An error occurred: %s" % session)
    return

print("Successfully authenticated: %s" % session)
```

{{< / code >}}

{{< code type="client" framework="defold">}}

```lua
local result = client.authenticate_custom(custom_id, nil, true, "mycustomusername")

if not result.token then
  print("Unable to login")
end

-- store the token and use it when communicating with the server
client.set_bearer_token(result.token)
```

{{< / code >}}

{{< code type="client" >}}

```swift
let id = "<customId>"
var create = true

do {
    let session = try await client.authenticateCustom(id: id, create: create, username: "mycustomusername")
    print("Successfully authenticated:", session)
} catch {
    debugPrint(error)
}
```

{{< / code >}}

{{< code type="client" >}}

```dart
final id = '<customId>';
final create = true;

final session = await client.authenticateCustom(
  id: id,
  create: create,
  username: 'mycustomusername',
);
```

{{< / code >}}

## Session

When an authentication call succeeds, the server responds with a [session](/docs/concepts/session/) object. The session object contains at least the following:

- The user ID
- The username
- A set of variables cached in the token
- The creation time
- The expiration time

Once the client obtains the session object, you can utilize Nakama's real-time features such as [multiplayer](../multiplayer/relayed/), [notifications](../notifications/) and [status updates](../status/), [passing stream data](../../server-framework/streams/) or [real-time chat](../chat/).

For details on token lifetimes, session refresh, and SDK auto-refresh support, see [Sessions](../session/).

{{< code type="client" >}}

```javascript
var socket = client.createSocket();
session = await socket.connect(session);
console.info("Socket connected.");
```

{{< / code >}}

{{< code type="client" >}}

```csharp
var socket = Socket.From(client);
await socket.ConnectAsync(session);
System.Console.WriteLine("Socket connected.");
```

{{< / code >}}

{{< code type="client" >}}

```cpp
int port = 7350; // different port to the main API port
bool createStatus = true; // if the server should show the user as online to others.
// define real-time client in your class as NRtClientPtr rtClient;
rtClient = client->createRtClient(port);
// define listener in your class as NRtDefaultClientListener listener;
listener.setConnectCallback([]()
{
    cout << "Socket connected." << endl;
});

rtClient->setListener(&listener);
rtClient->connect(session, createStatus);
```

{{< / code >}}

{{< code type="client" >}}

```java
SocketClient socket = client.createSocket();
socket.connect(session, new AbstractSocketListener() {}).get();
```

{{< / code >}}

{{< code type="client" framework="godot3" >}}

```gdscript
# Make this a node variable, or it will disconnect when the function that creates it returns.
onready var socket := Nakama.create_socket_from(client)

func _ready():
    var connected : NakamaAsyncResult = yield(socket.connect_async(session), "completed")

    if connected.is_exception():
        print("An error occurred: %s" % connected)
        return

    print("Socket connected.")
```

{{< / code >}}

{{< code type="client" framework="godot4" >}}

```gdscript
# Make this a node variable, or it will disconnect when the function that creates it returns.
@onready var socket := Nakama.create_socket_from(client)

func _ready():
    var connected : NakamaAsyncResult = await socket.connect_async(session)

    if connected.is_exception():
        print("An error occurred: %s" % connected)
        return

    print("Socket connected.")
```

{{< / code >}}

{{< code type="client" framework="defold">}}

```lua
local socket = client.create_socket()
local ok, err = socket.connect()

if not ok then
  print("Unable to connect: ", err)
end
```

{{< / code >}}

{{< code type="client" >}}

```swift
let socket = client.createSocket()
socket.connect(session, appearOnline: true) { error in
    if let error = error {
        print("Error connecting socket: \(error)")
        return
    }
    print("Socket connected.")
}
```

{{< / code >}}

{{< code type="client" >}}

```dart
// Socket is automatically connected after initialization
final socket = NakamaWebsocketClient(
  host: '127.0.0.1',
  port: 7350,
  ssl: false,
  token: '<token>',
);
```

{{< / code >}}

## Link or unlink

You can link one or more other login option to the current user. This makes it easy to support multiple logins with each user and easily identify a user across devices.

You can only link device Ids, custom Ids, and social provider IDs which are not already in-use with another user account.

{{< code type="client" >}}

```bash
curl "http://127.0.0.1:7350/v2/account/link/custom" \
  --header 'Authorization: Bearer $session' \
  --data '{"id":"some-custom-id"}'
```

{{< / code >}}

{{< code type="client" >}}

```shell
POST /v2/account/link/custom
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "id":"some-custom-id"
}
```

{{< / code >}}

{{< code type="client" >}}

```javascript
const customId = "some-custom-id";
const success = await client.linkCustom(session, customId);
console.log("Successfully linked custom ID to current user.");
```

{{< / code >}}

{{< code type="client" >}}

```csharp
const string customId = "some-custom-id";
await client.LinkCustomAsync(session, customId);
System.Console.WriteLine("Id '{0}' linked for user '{1}'", customId, session.UserId);
```

{{< / code >}}

{{< code type="client" >}}

```cpp
auto linkFailedCallback = [](const NError& error)
{
};

auto linkSucceededCallback = []()
{
    cout << "Linked successfully" << endl;
};

std::string customid = "some-custom-id";

client->linkCustom(customid, linkSucceededCallback, linkFailedCallback);
```

{{< / code >}}

{{< code type="client" >}}

```java
String customId = "some-custom-id";
client.linkCustom(session, customId).get();
System.out.format("Id %s linked for user %s", customId, session.getUserId());
```

{{< / code >}}

{{< code type="client" framework="godot3" >}}

```gdscript
var custom_id = "some-custom-id"
var linked : NakamaAsyncResult = yield(client.link_custom_async(session, custom_id), "completed")

if linked.is_exception():
    print("An error occurred: %s" % linked)
    return

print("Id '%s' linked for user '%s'" % [custom_id, session.user_id])
```

{{< / code >}}

{{< code type="client" framework="godot4" >}}

```gdscript
var custom_id = "some-custom-id"
var linked : NakamaAsyncResult = await client.link_custom_async(session, custom_id)

if linked.is_exception():
    print("An error occurred: %s" % linked)
    return

print("Id '%s' linked for user '%s'" % [custom_id, session.user_id])
```

{{< / code >}}

{{< code type="client" framework="defold">}}

```lua
local result = client.link_custom(custom_id)

if result.error then
  print("An error occurred:", result.error)
end
```

{{< / code >}}

{{< code type="client" >}}

```swift
let customId = "<customId>"
let success = try await client.linkCustom(session: session, id: customId)
print("Successfully linked custom ID to current user.")
```

{{< / code >}}

{{< code type="client" >}}

```dart
final customId = '<customId>';
await client.linkCustom(
  session: session,
  id: customId,
);
```

{{< / code >}}

You can unlink any linked login options for the current user.

{{< code type="client" >}}

```bash
curl "http://127.0.0.1:7350/v2/account/unlink/custom" \
  --header 'Authorization: Bearer $session' \
  --data '{"id":"some-custom-id"}'
```

{{< / code >}}

{{< code type="client" >}}

```shell
POST /v2/account/unlink/custom
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "id":"some-custom-id"
}
```

{{< / code >}}

{{< code type="client" >}}

```javascript
const customId = "some-custom-id";
const success = await client.unlinkCustom(session, customId);
console.info("Successfully unlinked custom ID from the current user.");
```

{{< / code >}}

{{< code type="client" >}}

```csharp
const string customId = "some-custom-id";
await client.UnlinkCustomAsync(session, customId);
System.Console.WriteLine("Id '{0}' unlinked for user '{1}'", customId, session.UserId);
```

{{< / code >}}

{{< code type="client" >}}

```cpp
auto unlinkFailedCallback = [](const NError& error)
{
};

auto unlinkSucceededCallback = []()
{
    cout << "Successfully unlinked custom ID from the current user." << endl;
};

std::string customid = "some-custom-id";

client->unlinkCustom(customid, unlinkSucceededCallback, unlinkFailedCallback);
```

{{< / code >}}

{{< code type="client" >}}

```java
String customId = "some-custom-id";
client.unlinkCustom(session, customId).get();
System.out.format("Id %s unlinked for user %s", customId, session.getUserId());
```

{{< / code >}}

{{< code type="client" framework="godot3" >}}

```gdscript
var custom_id = "some-custom-id"
var unlinked : NakamaAsyncResult = yield(client.unlink_custom_async(session, custom_id), "completed")

if unlinked.is_exception():
    print("An error occurred: %s" % unlinked)
    return

print("Id '%s' unlinked for user '%s'" % [custom_id, session.user_id])
```

{{< / code >}}

{{< code type="client" framework="godot4" >}}

```gdscript
var custom_id = "some-custom-id"
var unlinked : NakamaAsyncResult = await client.unlink_custom_async(session, custom_id)

if unlinked.is_exception():
    print("An error occurred: %s" % unlinked)
    return

print("Id '%s' unlinked for user '%s'" % [custom_id, session.user_id])
```

{{< / code >}}

{{< code type="client" framework="defold">}}

```lua
local result = client.unlink_custom(custom_id)

if result.error then
  print("An error occurred:", result.error)
end
```

{{< / code >}}

{{< code type="client" >}}

```swift
let customId = "<customId>"
let success = try await client.unlinkCustom(session: session, id: customId)
print("Successfully unlinked custom ID from the current user.")
```

{{< / code >}}

{{< code type="client" >}}

```dart
final customId = '<customId>';
await client.unlinkCustom(
  session: session,
  id: customId,
);
```

{{< / code >}}

You can link or unlink many different account options.

| Link | Description |
|
----------- | ----------------------------------------------------------------------------------- |
| Apple | An Apple account. |
| Custom | A custom identifier from another identity service. |
| Device | A unique identifier for a device which belongs to the user. |
| Email | An email and password set by the user. |
| Facebook | A Facebook social account. You can optionally import Facebook Friends upon linking. |
| Game Center | An account from Apple's Game Center service. |
| Google | A Google account. |
| Steam | An account from the Steam network. |
