# Sessions

**URL:** https://heroiclabs.com/docs/nakama/concepts/session/
**Summary:** A session represents a period during which a user is authenticated with the server. This session is represented with a client-side object that proves the client was authenticated. Sessions expire and become invalid after a period set in your server configuration.
**Keywords:** session details, session variables
**Categories:** nakama, session, concepts

---


# 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](../authentication/) when accessing server functions.

Session duration can be set with the [token_expiry_sec](../../getting-started/configuration/#common-properties) 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](../../getting-started/configuration/#session.refresh_token_expiry_sec), 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:

{{< missing type="client" lang="bash" />}}
{{< missing type="client" lang="shell" />}}

{{< code type="client" >}}
```javascript
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));
```
{{< / code >}}

{{< code type="client" >}}
```csharp
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);
```
{{< / code >}}

{{< code type="client" >}}
```cpp
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);
```
{{< / code >}}

{{< code type="client" >}}
```java
var deviceid = SystemInfo.deviceUniqueIdentifier;
Session session = client.authenticateDevice(deviceid).get();
System.out.format("Session %s", session.getAuthToken());
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
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)
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
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)
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
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
```
{{< / code >}}

{{< code type="client" >}}
```swift
let id = "3e70fd52-7192-11e7-9766-cb3ce5609916"
var session = await client.authenticateDevice(id: id)
debugPrint("Id: \(session.userId)", "Username: \(session.username)")
debugPrint("Session expired? \(session.isExpired)")
```
{{< / code >}}

{{< code type="client" >}}
```dart
final id = '3e70fd52-7192-11e7-9766-cb3ce5609916';
final session = await client.authenticateDevice(deviceId: id);
print('Id: ${session.userId}, Username: ${session.username}');
print('Session expired? ${session.isExpired}');
```
{{< / code >}}

## Session refresh

{{< missing type="client" lang="bash" />}}
{{< missing type="client" lang="java" />}}

{{< code type="client" >}}
```cpp
client->authenticateRefresh(session);
```
{{< / code >}}

{{< code type="client" lang="lua" framework="defold" >}}
```lua
client.session_refresh(session.refresh_token)
```
{{< / code >}}

{{< code type="client" >}}
```shell
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"
  }
}
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
session = yield(client.session_refresh_async(session), "completed)
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
session = await client.session_refresh_async(session)
```
{{< / code >}}

{{< code type="client" >}}
```javascript
session = await client.sessionRefresh(session);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
session = await client.SessionRefreshAsync(session);
```
{{< / code >}}

{{< code type="client" >}}
```swift
session = await client.refreshSession(session: session)
```
{{< / code >}}

{{< code type="client" >}}
```dart
session = await client.sessionRefresh(session: session);
```
{{< / code >}}

You can optionally provide new [session variable](#session-variables) 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.

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

{{< code type="client" lang="lua" framework="defold" >}}
```lua
client.session_logout(session.refresh_token, session.token)
```
{{< / code >}}

{{< code type="client" >}}
```shell
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>"
}
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
yield(client.session_logout_async(session), "completed")
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
await client.session_logout_async(session)
```
{{< / code >}}

{{< code type="client" >}}
```javascript
await client.sessionLogout(session);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
await client.SessionLogoutAsync(session);
```
{{< / code >}}

{{< code type="client" >}}
```swift
await client.sessionLogout(session: session)
```
{{< / code >}}

{{< code type="client" >}}
```dart
await client.sessionLogout(session: session);
```
{{< / code >}}

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](../../server-framework/typescript-runtime/function-reference/#Sessions).

## Session variables

Session variables allow clients and server-side code to embed additional key/value pairs into the session token generated by the game backend. 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](#session-refresh) 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:

{{< missing type="client" lang="cpp" />}}
{{< missing type="client" lang="java" />}}
{{< missing type="client" framework="godot3" lang="gdscript" />}}
{{< missing type="client" framework="godot4" lang="gdscript" />}}

{{< code type="client" framework="defold">}}
```lua
local vars = { key = "value" }
local create = true
local result = client.authenticate_email(email, password, vars, create, "mycustomusername")
```
{{< / code >}}

{{< code type="client" >}}
```java
Map<String, String> vars = new HashMap<>();
vars.put("Key", "Value");
vars.put("Key2", "Value2");

Session session = client.authenticateEmail("<email>", "<password>", vars).get();
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var vars = {
    "key" : "value",
    "key2" : "value2"
    # ...
}

var session : NakamaSession = yield(client.authenticate_email_async("<email>", "<password>", null, true, vars), "completed")
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var vars = {
    "key" : "value",
    "key2" : "value2"
    # ...
}

var session : NakamaSession = await client.authenticate_email_async("<email>", "<password>", null, true, vars)
```
{{< / code >}}

{{< code type="client" >}}
```csharp
var vars = new Dictionary<string, string>();
vars["key"] = "value";
vars["key2"] = "value2";
// ...
var session = await client.AuthenticateEmailAsync("<email>", "<password>", null, true, vars);
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const create = true;
const session = await client.authenticateEmail(email, password, create, "mycustomusername", {key: "value"});
console.info("Successfully authenticated:", session);
```
{{< / code >}}

{{< code type="client" >}}
```bash
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" }}'
```
{{< / 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: Bearer <session token>
{
    "email": "hello@heroiclabs.com",
    "password": "password",
    "vars": {
        "key": "value"
    }
}
```
{{< / code >}}

{{< code type="client" >}}
```swift
let vars: [String : String] = [
    "key": "value",
    "key2": "value2"
]
// ...
let session = await client.authenticateEmail(email: "<email>", password: "<password>", create: false, username: nil, vars: vars)
```
{{< / code >}}

{{< code type="client" >}}
```dart
final Map<String, String> vars = {
  'key': 'value',
  'key2': 'value2',
};
// ...
final session = await client.authenticateEmail(
  email: '<email>',
  password: '<password>',
  create: false,
  username: null,
  vars: vars,
);
```
{{< / code >}}

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

### Setting with server

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

{{< code type="server" >}}
```lua
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")
```
{{< / code >}}

{{< code type="server" >}}
```go
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
}
```
{{< / code >}}

{{< code type="server" >}}
```typescript
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);
}
```
{{< / code >}}


### 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 type="server" >}}
```typescript
let accessSessionVars: nkruntime.BeforeHookFunction<nkruntime.GetAccountRequest> = function (
  context: nkruntime.Context,
  logger: nkruntime.Logger,
  nk: nkruntime.Nakama,
  payload: nkruntime.GetAccountRequest
) {
  if (context.vars) {
    logger.info("User session contains key-value pairs set by both the client and the before authentication hook: %q", context.vars);
  } else {
    logger.info("No session variables were found.");
  }

  return payload;
}

let InitModule: nkruntime.InitModule = function (
  ctx: nkruntime.Context,
  logger: nkruntime.Logger,
  nk: nkruntime.Nakama,
  initializer: nkruntime.Initializer
) {
  initializer.registerBeforeGetAccount(accessSessionVars);
}
```
{{< / code >}}

{{< code type="server" >}}
```lua
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")
```
{{< / code >}}

{{< code type="server" >}}
```go
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
}
```
{{< / code >}}
