Sessions #

In general terms, a “session” is a period during which a user is authenticated with a system.

In Nakama, the session is represented with a client-side object that proves the client was authenticated when accessing server functions.

Session duration can be set with the token_expiry_sec property in the configuration. It is recommended to set a session duration of about 2 to 3 times the length of your game’s average play session.

Sessions expire and become invalid after this set period. A refresh token is provided when the client authenticates, enabling you to obtain new auth tokens for as long as the refresh token remains valid. When the refresh token expires, you’ll need to reauthenticate with the server to get a new session.

Session details #

You can access a user’s id, name, and whether their session is expired using the following code:

Code snippet for this language cURL has not been found. Please choose another language to show equivalent examples.
Code snippet for this language REST has not been found. Please choose another language to show equivalent examples.
Code snippet for this language Swift has not been found. Please choose another language to show equivalent examples.

Client
1
2
3
4
const id = "3e70fd52-7192-11e7-9766-cb3ce5609916";
const session = await client.authenticateDevice(id)
console.info("id:", session.user_id, "username:", session.username);
console.info("Session expired?", session.isexpired(Date.now() / 1000));
Client
1
2
3
4
const string id = "3e70fd52-7192-11e7-9766-cb3ce5609916";
var session = await client.AuthenticateDeviceAsync(id);
System.Console.WriteLine("Id '{0}' Username '{1}'", session.UserId, session.Username);
System.Console.WriteLine("Session expired? {0}", session.IsExpired);
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
auto loginFailedCallback = [](const NError& error)
{
};

auto loginSucceededCallback = [](NSessionPtr session)
{
    cout << "id " << session->getUserId() << " username " << session->getUsername() << endl;
    cout << "Session expired? " << (session->isExpired() ? "yes" : "no") << endl;
};

std::string deviceId = "3e70fd52-7192-11e7-9766-cb3ce5609916";

client->authenticateDevice(deviceId, opt::nullopt, opt::nullopt, {}, loginSucceededCallback, loginFailedCallback);
Client
1
2
3
var deviceid = SystemInfo.deviceUniqueIdentifier;
Session session = client.authenticateDevice(deviceid).get();
System.out.format("Session %s", session.getAuthToken());
Client
1
2
3
4
5
6
7
8
var session : NakamaSession = yield(client.authenticate_device_async(deviceid), "completed")

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

print("Id '%s' Username '%s'" % [session.id, session.username])
print("Session expired? %s" % session.expired)
Client
1
2
3
4
5
6
7
8
var session : NakamaSession = await client.authenticate_device_async(deviceid)

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

print("Id '%s' Username '%s'" % [session.id, session.username])
print("Session expired? %s" % session.expired)
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
local result = client.authenticate_device(defold.uuid())

if result.error then
  print(result.message)
  return
end

print(("Id '%s' Username '%s' "):format(result.user_id, result.username))

if os.time() > result.expires then
  print("Session has expired")
end

Session refresh #

Code snippet for this language cURL has not been found. Please choose another language to show equivalent examples.
Code snippet for this language Java/Android has not been found. Please choose another language to show equivalent examples.
Code snippet for this language Swift has not been found. Please choose another language to show equivalent examples.

Client
1
client->authenticateRefresh(session);
Client
1
client.session_refresh(session.refresh_token)
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
POST /v2/session/refresh/
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Basic base64(ServerKey:)
{
  "token": "<Refresh token>",
  "vars": {
    "key": "value",
    "key2": "value2"
  }
}
Client
1
session = yield(client.session_refresh_async(session), "completed)
Client
1
session = await client.session_refresh_async(session)
Client
1
session = await client.sessionRefresh(session);
Client
1
session = await client.SessionRefreshAsync(session);

You can optionally provide new session variable values when refreshing a session. These new values will replace any session variables currently stored in the session object.

Session logout #

It is good security practice to enable users to logout of an active session. This can be used to prevent unauthorized access by another person when using a shared device, for example.

Code snippet for this language cURL has not been found. Please choose another language to show equivalent examples.
Code snippet for this language C++/Unreal/Cocos2d-x has not been found. Please choose another language to show equivalent examples.
Code snippet for this language Java/Android has not been found. Please choose another language to show equivalent examples.
Code snippet for this language Swift has not been found. Please choose another language to show equivalent examples.

Client
1
client.session_logout(session.refresh_token, session.token)
Client
1
2
3
4
5
6
7
8
9
POST /v2/session/logout/
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Basic base64(ServerKey:)
{
  "token": "<Session token to log out>",
  "refreshToken": "<Refresh token to invalidate>"
}
Client
1
yield(client.session_logout_async(session), "completed")
Client
1
await client.session_logout_async(session)
Client
1
await client.sessionLogout(session);
Client
1
await client.SessionLogoutAsync(session);

Logging out of a user’s session invalidates their authentication and refresh tokens, but does not disconnect their open socket connections. This can be accomplished via the server-side sessionDisconnect function. See the corresponding function reference.

Session variables #

Session variables allow clients and server-side code to embed additional key/value pairs into the session token generated by the game server. This enables the session token to act as an edge cache, with the information available as long as the session remains active.

These can be used to implement different features and tools, such as analytics, referral bonuses, or rewards programs.

You can optionally provide new session variable values when refreshing a session. These new values will replace any session variables currently stored in the session object.

Setting with client #

To set the variables in the client’s request for authentication use:

Code snippet for this language Swift has not been found. Please choose another language to show equivalent examples.
Code snippet for this language C++/Unreal/Cocos2d-x has not been found. Please choose another language to show equivalent examples.
Code snippet for this language Java/Android has not been found. Please choose another language to show equivalent examples.
Code snippet for this language Godot 3 has not been found. Please choose another language to show equivalent examples.
Code snippet for this language Godot 4 has not been found. Please choose another language to show equivalent examples.

Client
1
2
3
local vars = { key = "value" }
local create = true
local result = client.authenticate_email(email, password, vars, create, "mycustomusername")
Client
1
2
3
4
5
Map<String, String> vars = new HashMap<>();
vars.put("Key", "Value");
vars.put("Key2", "Value2");

Session session = client.authenticateEmail("<email>", "<password>", vars).get();
Client
1
2
3
4
5
6
7
var vars = {
    "key" : "value",
    "key2" : "value2"
    # ...
}

var session : NakamaSession = yield(client.authenticate_email_async("<email>", "<password>", null, true, vars), "completed")
Client
1
2
3
4
5
6
7
var vars = {
    "key" : "value",
    "key2" : "value2"
    # ...
}

var session : NakamaSession = await client.authenticate_email_async("<email>", "<password>", null, true, vars)
Client
1
2
3
4
5
var vars = new Dictionary<string, string>();
vars["key"] = "value";
vars["key2"] = "value2";
// ...
var session = await client.AuthenticateEmailAsync("<email>", "<password>", null, true, vars);
Client
1
2
3
const create = true;
const session = await client.authenticateEmail(email, password, create, "mycustomusername", {key: "value"});
console.info("Successfully authenticated:", session);
Client
1
2
3
curl "http://127.0.0.1:7350/v2/account/authenticate/email?create=true&username=mycustomusername" \
--user 'defaultkey:' \
--data '{"email": "hello@heroiclabs.com", "password": "password", "vars": { "key": "value" }}'
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
POST /v2/account/authenticate/email?create=true&username=mycustomusername
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
    "email": "hello@heroiclabs.com",
    "password": "password",
    "vars": {
        "key": "value"
    }
}

This example shows setting session variables with Email Authentication, but this feature is available for all of the other authentication options and in all client libraries.

Setting with server #

Session variables may be set by the server, but only in the before authentication hooks.

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
local nk = require("nakama")

local function set_session_vars(context, payload)
  nk.logger_info("User session contains key-value pairs set by the client: " .. nk.json_encode(payload.account.vars))

  payload.account.vars["key_added_in_lua"] = "value_added_in_lua"
  return payload
end

nk.register_req_before(set_session_vars, "AuthenticateEmail")
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
func InitModule(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, initializer runtime.Initializer) error {
    if err := initializer.RegisterBeforeAuthenticateEmail(SetSessionVars); err != nil {
        logger.Error("Unable to register: %v", err)
        return err
    }

    return nil
}

func SetSessionVars(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, in *api.AuthenticateEmailRequest) (*api.AuthenticateEmailRequest, error) {
    logger.Info("User session contains key-value pairs set the client: %v", in.GetAccount().Vars)

    if in.GetAccount().Vars == nil {
        in.GetAccount().Vars = map[string]string{}
    }

    in.GetAccount().Vars["key_added_in_go"] = "value_added_in_go"

    return in, nil
}
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let setSessionVars: nkruntime.BeforeHookFunction<nkruntime.AuthenticateEmailRequest> = function (context: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, payload: nkruntime.AuthenticateEmailRequest) {
  payload.account.vars = {
    key: 'key',
    value: 'value',
  }

  return payload;
}

function InitModule(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, initializer: nkruntime.Initializer) {
  initializer.registerBeforeAuthenticateEmail(setSessionVars);
}

Accessing with server #

Once set, session variables become read-only and may be accessed in any server hook. The only way to change or delete a session variable is through forcing the client to authenticate again.

Code snippet for this language TypeScript has not been found. Please choose another language to show equivalent examples.
Server
1
2
3
4
5
6
7
8
9
local nk = require("nakama")

local function access_session_vars(context, payload)
  nk.logger_info("User session contains key-value pairs set by both the client and the before authentication hook: " .. nk.json_encode(context.vars))

  return payload
end

nk.register_req_before(access_session_vars, "GetAccount")
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
func InitModule(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, initializer runtime.Initializer) error {
    if err := initializer.RegisterBeforeGetAccount(AccessSessionVars); err != nil {
        logger.Error("Unable to register: %v", err)
        return err
    }

    return nil
}

func AccessSessionVars(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule) error {
    vars, ok := ctx.Value(runtime.RUNTIME_CTX_VARS).(map[string]string)

    if !ok {
        logger.Info("User session does not contain any key-value pairs set")
        return nil
    }

    logger.Info("User session contains key-value pairs set by both the client and the before authentication hook: %v", vars)
    return nil
}

Related Pages