Skip to main content

Session Keep-Alive and Duplicate Login Check

This API periodically sends session keep-alive signals to the server and checks for duplicate logins from other devices.

Example Code Note

The example code in this document is provided for reference. The callback invocation method using Update() checks every frame, so calling the callback directly within the coroutine may be more efficient. When applying this to your actual project, feel free to modify it according to your project's architecture and coding conventions.

URL Confirmation

This API uses the service-account.playnanoo.com domain.

API Information

  • URL: https://service-account.playnanoo.com/api/v20240401/alive
  • Method: PUT
  • Authentication Required: Yes

Request Parameters

ParameterTypeRequiredDescription
platformstringRequiredPlatform (e.g., "aos", "ios")
device_idstringRequiredDevice unique ID
device_modelstringRequiredDevice model name
device_osstringRequiredDevice OS
device_languagestringRequiredDevice language (e.g., "KO", "EN")
DeviceInfo Inheritance

The Req class for this API inherits from DeviceInfo. All properties of DeviceInfo are automatically included.

Response Data

Success Response

FieldTypeDescription
StatusstringStatus (success: "success")

Error Response

Error CodeDescription
30006DuplicatedDeviceException - Already authenticated on another device

Unity C# Implementation

CheckAlive Class

using System;
using System.Collections;
using UnityEngine.Networking;

public class CheckAlive
{
static string path = "https://service-account.playnanoo.com/api/v20240401/alive";

[Serializable]
public class Req : DeviceInfo
{
public IEnumerator Send(
Action<Res> onSuccess,
Action<BaseResponse> onError)
{
yield return HttpClient.Send<Req, Res>(
UnityWebRequest.kHttpVerbPUT,
path,
requireToken: true,
body: this,
onSuccess: onSuccess,
onError: onError
);
}
}

[Serializable]
public class Res : BaseResponse
{
public string Status;
}
}

Add to Singleton Manager Class

Add periodic CheckAlive calls and duplicate login detection to your singleton manager.

// Variable declarations
private bool _isDuplicate = false;
private Coroutine _aliveCoroutine;
private Action<bool> _duplicateCallback;

void Update()
{
// If duplicate callback is registered and duplicate is detected, invoke callback
if (_duplicateCallback != null && _isDuplicate)
{
_duplicateCallback.Invoke(true);
_isDuplicate = false; // Reset after one call
}
}

// Start session keep-alive (minimum 300 seconds)
public void CheckAliveStart(int delayTime = 300)
{
if (_aliveCoroutine != null) return;
int timer = delayTime < 300 ? 300 : delayTime;
_aliveCoroutine = StartCoroutine(AliveStatus(timer));
}

// Stop session keep-alive
public void CheckAliveStop()
{
if (_aliveCoroutine != null)
{
StopCoroutine(_aliveCoroutine);
_aliveCoroutine = null;
}
}

IEnumerator AliveStatus(float delayTime)
{
while (true)
{
yield return new WaitForSeconds(delayTime);

if (string.IsNullOrEmpty(DataManager.Instance.AccessToken)) continue;

CheckAlive.Req req = new CheckAlive.Req();
yield return req.Send(
onSuccess: res =>
{
// Success response - not duplicate
},
onError: err =>
{
// 30006: DuplicatedDeviceException - Already authenticated on another device
if (err != null && err.ErrorCode == "30006")
{
_isDuplicate = true;
}
}
);
}
}

// Register duplicate login check callback
public void CheckDuplicate(Action<bool> callback)
{
_duplicateCallback = callback;
}

Usage Example

Basic Usage

void Start()
{
// Start session keep-alive after successful login (every 300 seconds)
YourSingleton.Instance.CheckAliveStart(300);

// Register duplicate login check callback
YourSingleton.Instance.CheckDuplicate(OnCheckAccountDuplicate);
}

void OnCheckAccountDuplicate(bool isDuplicate)
{
if (isDuplicate)
{
Debug.LogError("Duplicate connection has been detected.");
// Force logout processing
// e.g., Navigate to login screen, delete tokens, etc.
}
}

Stop Session Keep-Alive on Logout

public void Logout()
{
// Stop session keep-alive
YourSingleton.Instance.CheckAliveStop();

// Other logout processing...
}

Flow

  1. After successful login, call CheckAliveStart(300)
  2. Every 300 seconds, send alive request to server
  3. Success response: Session maintained
  4. Error 30006: Logged in from another device → _isDuplicate = true
  5. Update(): When duplicate detected, invoke registered callback
  6. Callback processing: Force logout, show notification, etc.

Caution

Do Not Call Multiple Times

CheckAliveStart() should be called only once in your entire application.

  • Do not call it from multiple scenes or components.
  • Multiple calls may cause the server to detect abnormal request patterns, resulting in false positive duplicate login errors (30006).
  • Use a singleton pattern to manage session keep-alive from a single location.
  • While the implementation internally prevents duplicate calls when a timer is already running, it's best practice to maintain a single call point structurally.