WEB Authentication via Server and Browser (GSID, Game Session ID)

Core Concept

The primary goal of this authentication method is to enable any game client to leverage the full capabilities of web-based authentication without relying on less secure mechanisms such as redirect-based flows or embedded web browsers.

To use this authentication method, the game client must:

  • Be capable of making POST requests to the SSO service.

  • Be able to open an external or embedded browser while passing a GET request.


Authentication Workflow

  1. The client sends a request to /proxy/api/gameSession/getGsid, signaling its intent to initiate authentication. The SSO server registers a temporary authentication session and returns a session identifier to the client.

  2. The client opens the web authentication page in a browser, passing the session identifier in the GET request.

  3. Simultaneously, the client begins polling /proxy/api/gameSession/getGsidResult, providing the session identifier.

  4. Once the user successfully logs in via the browser, the game client receives the user’s credentials in response to its request.

  5. After validating the received credentials (if necessary), the client informs the SSO server that it has obtained and successfully used them.

  6. The login page in the browser notifies the user that authentication is complete and user may return to the game client window. This step is optional for authentication itself but ensures that users who have opened the browser in full-screen mode and cannot see the game client receive confirmation that the process is complete and they can resume gameplay.


Protocol Specification

Register Authentication Session

Registers a new authentication session and returns a session identifier. The session remains valid for 2 minutes and can be extended up to 20 minutes by repeated polling of /proxy/api/gameSession/getGsidResult.

Request

POST /proxy/api/gameSession/getGsid

  • factor string optional

    Device factor for JWT generation. Required if the client needs a JWT as part of authentication.

    See also

    For more information, see JSON Web Tokens (JWT).

  • public_key string optional

    Base64-encoded RSA public key. If provided, subsequent requests must include a signature in the Request-Sign header instead of matching the originating IP address. See Signed Requests.

  • jwt_public_key string optional

    RSA public key to embed in the user’s JWT (for mobile apps).

  • distr string optional

    Distributor tag associated with the session.

  • app_id integer optional

    Gaijin Central application ID for which user permissions are retrieved. Default: 0 (not specified).

  • gapp_id integer optional

    Gaijin game application ID for which user permissions are retrieved.

  • nojwt string optional

    Set to "1" to disable JWT issuance. Default: "0".

  • service_jwt string optional

    Service JWT for obtaining user permissions.

  • short_token string optional

    Set to "1" to return a short-lived token. The token is bound to the IP address from which the authentication session was initiated. Default: "0".

  • fixed_ip string optional

    Set to "1" to enforce a fixed IP address in the user’s JWT. Default: "0".

  • fixed_ips[] array of strings optional

    List of allowed IP ranges in CIDR format. If no subnet mask is specified, a /32 mask is applied. All ranges must belong to private networks.

  • meta string optional

    Set to "1" to include meta tags in the authentication result response. Default: "0".

  • with_email string optional

    Secret hash to enable retrieval of the user’s login email in the authentication result.

Request example
{
  "factor": "unique-client-string",
  "public_key": "<BASE64-encoded RSA public key>",
  "app_id": 12345,
  "gapp_id": 67890,
  "meta": "1"
}

Response

Success

  • status string required

    • “OK”

  • gsid string required

    Session identifier to use in subsequent requests.

  • ts integer required

    Current SSO server timestamp (Unix epoch).

Response example
{
  "status": "OK",
  "gsid": "4031ac2f-4ee0-4079-88cc-dd210e9a7356",
  "ts": 1700000000
}

Error

  • status string required

    • “ERROR”

  • error string required

    Descriptive error message.

Response example
{
  "status": "ERROR",
  "error": "Wrong request"
}

Get Authentication Results

Polls for the result of an authentication session. The server holds the request open for up to 30 seconds before responding with RETRY. Each request extends the session lifespan by 2 minutes (up to the 20-minute maximum). The client must keep polling until a definitive response is received.

Request

POST /proxy/api/gameSession/getGsidResult

Request example
{
  "gsid": "4031ac2f-4ee0-4079-88cc-dd210e9a7356"
}

Security Note

Since the session identifier is passed in plain text when opening the browser, it may become accessible to anyone who sees the authorization URL on the screen. This is a common risk, particularly for streamers. To mitigate this risk, the following restrictions apply to the /proxy/api/gameSession/getGsidResult request:

  • The request must originate from the same IP address as the /proxy/api/gameSession/getGsid request.

  • Alternatively, the request must include the Location parameter in the URL, as returned to the client in the Location response header at the session start.

  • Another option is to provide an encryption key (public_key) in the /proxy/api/gameSession/getGsid request (either in the request payload or via the Public-Key HTTP header). All subsequent HTTP requests must then include a signature in the Request-Sign header (see Signed Requests).

The parameters listed above can be passed using the following HTTP headers:

  • Authorization

  • Location

  • Ip <ipAddress>

  • Sign <sign> along with public_key in the request body (or via the Public-Key HTTP header).

Response

Success — authentication complete

  • status string required

    • “OK”

  • gsid string required

    Game session identifier.

  • ts integer required

    Current SSO server timestamp (Unix epoch).

  • started integer required

    Session creation timestamp (Unix epoch).

  • last_seen integer required

    Timestamp of the last WebSocket heartbeat (Unix epoch). Updated every 5 seconds while a WebSocket connection to /ws/gameSession/refresh/<gsid> is active. 0 if no WebSocket connection was established.

The response also includes the full authentication data returned by the SSO login or registration operation (same fields as the /login.php response).

Response example
{
  "status": "OK",
  "gsid": "4031ac2f-4ee0-4079-88cc-dd210e9a7356",
  "ts": 1700000000,
  "started": 1699999999,
  "last_seen": 1700000050,
  "token": "...",
  "token_exp": 3600,
  "user_id": "119669708",
  "gjnick": "username"
}

Retry — authentication not yet complete

  • status string required

    • “RETRY”

  • gsid string required

    Game session identifier.

  • ts integer required

    Current SSO server timestamp (Unix epoch).

  • started integer required

    Session creation timestamp (Unix epoch).

  • last_seen integer required

    Timestamp of the last WebSocket heartbeat (Unix epoch). Updated every 5 seconds while a WebSocket connection to /ws/gameSession/refresh/<gsid> is active. 0 if no WebSocket connection was established.

  • delay integer required

    Number of seconds to wait before retrying. The client must respect this delay even when the value is 0. Failure to comply may cause the SSO system to block requests from the current IP address or key.

Response example
{
  "status": "RETRY",
  "gsid": "4031ac2f-4ee0-4079-88cc-dd210e9a7356",
  "ts": 1700000000,
  "started": 1699999999,
  "last_seen": 1700000050,
  "delay": 0
}

Cancelled — WebSocket connection closed

  • status string required

    • “CANCELLED”

  • gsid string required

    Game session identifier.

  • ts integer required

    Current SSO server timestamp (Unix epoch).

  • started integer required

    Session creation timestamp (Unix epoch).

  • last_seen integer required

    Timestamp of the last WebSocket heartbeat (Unix epoch).

Response example
{
  "status": "CANCELLED",
  "gsid": "4031ac2f-4ee0-4079-88cc-dd210e9a7356",
  "ts": 1700000000,
  "started": 1699999999,
  "last_seen": 1700000050
}

Error

  • status string required

    • “ERROR”

  • error string required

    Internal log record identifier. To determine the exact issue, locate the corresponding record in the SSO system logs.

Response example
{
  "status": "ERROR",
  "error": "a1b2c3d4"
}

Polling Behavior

  • HTTP request timeout on the client: Retry the request immediately.

  • status="RETRY": Wait the number of seconds specified in delay (including 0), then retry.

  • status="ERROR": Further requests for this gsid are not useful.

  • HTTP 403 Forbidden with status="ERROR": Rejected for security reasons. See Security Protocol Violations.

  • WebSocket connection closed: An open poll request receives status="CANCELLED". See Update Game Session Activity Timestamp.


Security Protocol Violations

If the security protocol is violated, the request is terminated with an HTTP 403 Forbidden response.

Possible reasons (non-exhaustive list):

  • The gsid session does not exist.

  • The IP address does not match the session’s expected IP.

  • The signature for the session is incorrect.

  • The location value in the request URL is incorrect.

  • A repeated attempt to obtain authentication data is made.

To prevent potential attackers from obtaining unnecessary information about what they are doing wrong, the error field in the response contains an internal log record identifier instead of an error description or code. To determine the exact issue, it is necessary to locate the corresponding record in the SSO system logs.


Signed Requests

If there is concern that the user’s IP address might change, it is better to use signed requests. In this case, the IP address is not checked at all.

The request includes a signature of the request body, created using a secret key generated at the start of the authentication session.

Signature Generation (PHP)

$keypair = openssl_pkey_new(array("private_key_bits" => 1024));
openssl_pkey_export($keypair, $privateKey);

$publicKey = openssl_pkey_get_details($keypair);
$publicKey = $publicKey["key"];

// Example request body
$payload = '{"gsid":"jhgf76rtry6"}';

openssl_sign($payload, $signature, openssl_pkey_get_private($privateKey), "sha256WithRSAEncryption");

header('Request-Sign: ' . base64_encode($signature));

Get Game Session Data

Retrieves game session data (excluding user information). For example, this allows obtaining the distr value based on gsid. Requests are only accepted from clients whitelisted by IP.

Request

POST /proxy/api/gameSession/info

  • gsid string required

    Game session identifier.

Request example
{
  "gsid": "4031ac2f-4ee0-4079-88cc-dd210e9a7356"
}

Response

Success

  • status string required

    • “OK”

  • session object required

    Game session data (without user authentication data). Key fields:

    • distr string required

      Distributor tag.

    • factor string required

      Device factor used at session creation.

    • app_id integer required

      Gaijin Central application ID.

    • gapp_id integer required

      Gaijin game application ID.

    • ip string required

      Client IP address at session creation.

    • created integer required

      Session creation timestamp (Unix epoch).

    • last_seen integer required

      Timestamp of the last WebSocket heartbeat (Unix epoch). 0 if no WebSocket connection was established.

    • state string required

      Session status ("Success" or "Error").

Response example
{
  "status": "OK",
  "session": {
    "distr": "steam",
    "factor": "unique-client-string",
    "app_id": 12345,
    "gapp_id": 0,
    "ip": "192.168.1.1",
    "created": 1699999999,
    "last_seen": 1700000050,
    "state": ""
  }
}

Error

  • status string required

    • “ERROR”

  • error string required

    Descriptive error message.

Response example
{
  "status": "ERROR",
  "error": "game session not found"
}

Close Session

After obtaining and using the user’s credentials, notify the SSO server about whether the credentials were successfully used. This informs the user that they can switch back from the browser to the game, and in some cases the authorization window can be closed automatically.

If encryption keys are used, this request must also be signed (see Signed Requests).

Note

The game session is not deleted by this request. It is removed automatically based on TTL.

Request

POST /proxy/api/gameSession/closeGsid

  • gsid string required

    Game session identifier.

  • status string required

    Outcome of the authentication attempt. Allowed values: "Success", "Error".

  • error string optional

    Description of the issue when status is "Error".

Request example
{
  "gsid": "4031ac2f-4ee0-4079-88cc-dd210e9a7356",
  "status": "Success"
}

Response

Success

  • status string required

    • “OK”

Response example
{
  "status": "OK"
}

Error

  • status string required

    • “ERROR”

  • error string required

    Descriptive error message.

Response example
{
  "status": "ERROR",
  "error": "Invalid status value"
}

Open Authentication in Browser

Upon receiving a session identifier from the /getGsid request, the client launches the browser using any available method, passing an additional request parameter: gsid=<ID>.

Example:

https://login.gaijin.net/?gsid=4031ac2f-4ee0-4079-88cc-dd210e9a7356

In this case, all parameters are processed as usual. However, if any parameters were provided when the game session was created, those values will override parameters passed when calling the authentication.


Update Game Session Activity Timestamp

wss://login.gaijin.net/ws/gameSession/refresh/<gsid:string> (WebSocket connection)

When the connection is established, the last_seen field of the game session is updated every 5 seconds. The connection may be terminated by either the client or the server (e.g., if the game session is not found, due to a server error, etc.). Details of the termination will be included in the event body.

If the client terminates the connection, an open request for retrieving session results will receive an event with the status CANCELLED.


Usage Example (Test Client)

Build Instructions

  1. Install Go (latest version recommended) from https://golang.org/dl/

  2. Clone the mairon repository (access available on request):

    git clone "ssh://<repository-access>" && scp -p -P 29418 <user>@<server>:hooks/commit-msg "mairon/.git/hooks/"
    
  3. Navigate to the code directory:

cd mairon/sso/examples/web-auth-login
  1. Build the example:

go build -v .
  1. Run and check available options:

./web-auth-login --help

Usage of web-auth-login

./web-auth-login --help

Available options:

    --distr string           Add the specified distr to the register URL
    --gsid string            Use an already existing session ID
    --keep-open              Don't close session after successful auth
    --no-factor              Don't send a custom factor for JWT generation
    --no-jwt                 Pass 'nojwt' auth option on login
    --no-sign-request        Don't use request signing
    --register-page string   (default "https://login.gaijin.net/fromgame")
    --short-token            Pass 'short_token' auth option on login
-s, --sso-url string         (default "https://login.gaijin.net")

The workflow of web-auth-login is straightforward:

  1. Create a GSID session on the SSO server using the required command-line parameters.

  2. Display the session ID to the user along with the recommended browser URL.

  3. Wait until the authentication data is returned for the specified session ID.


Examples

Authentication Without JWT, but With Short Token

./web-auth-login --no-jwt --no-factor --short-token -s https://login.gaijin.net
Output
2021/06/30 13:18:41 auth option: nojwt
2021/06/30 13:18:41 auth option: short_token
2021/06/30 13:18:41 initiating new session
2021/06/30 13:18:41 session created: id=b80551d0-48eb-4d9c-949c-ebd22f80f92f (at 2021-06-30 10:18:41 +0000 UTC)
2021/06/30 13:18:41 please open a browser at: https://login.gaijin.net?gsid=b80551d0-48eb-4d9c-949c-ebd22f80f92f
2021/06/30 13:18:41 attempt to get auth result
2021/06/30 13:19:11 RETRY after 0s
2021/06/30 13:19:11 attempt to get auth result
2021/06/30 13:19:42 RETRY after 0s
2021/06/30 13:19:42 attempt to get auth result
2021/06/30 13:19:58 SUCCESS: got session result
2021/06/30 13:19:58
{
  "auth": "login",
  "country": "RU",
  "gjnick": "kihytgcjytcr",
  "lang": "en",
  "level": "fastregister",
  "login": "g.yaltchik+js8756fc@gmail.com",
  "meta": {
    "geo_city": "Zelenogorsk",
    "geo_country": "RU",
    "ip": "91.236.238.168",
    "r_tags": "sso,lang_en",
    "type_registration": "email"
  },
  "nick": "kihytgcjytcr",
  "reg_time": "1624982700",
  "status": "OK",
  "stoken": "ba2e*******7304",
  "tags": "lang_en,sso",
  "token": "l692************9o8k",
  "token_exp": 3600,
  "user_id": "119669708"
}

Authentication on Test SSO with JWT, Using Randomly Generated Factor

./web-auth-login -s https://login-sso-test.gaijin.net