- Last Updated
Web Service SSO Integration
Understanding
SSO integration is a feature that allows games to open web services such as customer support, terms of service, events, and shops without requiring a re-login. It is structured to issue a one-time state based on the game AccessToken obtained through game login, and then call the SSO Callback to deliver authentication cookies to the web service.
There are two integration methods. Games using Mobile/PC SDKs use the SDK method (default), where the SDK handles state issuance and Callback calls, while some games that do not use the SDK use the direct API method (exception), where they call the APIs directly.
Integration Methods
| Method | Used in these cases | Features |
|---|---|---|
| SDK Method (Default) |
Games already using Mobile SDK or PC SDK | The SDK provides token verification, state issuance, and webview execution as a standard flow. Simple to implement and recommended method |
| Direct API Method (Exception) |
Some games that do not use the SDK and call SSO APIs directly | The game directly implements state issuance, Callback calls, and token renewal. High flexibility, but requires greater implementation and maintenance responsibility |
Application Environment
SSO is divided into webview flows and external browser flows depending on where the web service is displayed. Even if the platform is different, the Callback structure is the same, with only the execution method and issued cookies differing.
| Classification | Webview Flow | External Browser Flow |
|---|---|---|
| Execution Location | Embedded webview within the game screen (Android WebView, iOS WKWebView, CEF, WebView2) | User's default browser |
| Token Type (mode) | game_token | web_token |
| Issued Cookie | Access Token (SUAT) | Access Token (SUAT) + Refresh Token (SURT) |
Components
| Component | Role |
|---|---|
| Game Client (SDK/Direct) | Verifies game AccessToken, issues state, generates SSO Callback URL, and launches WebView/external browser |
| Sim API (SSO Gateway) | Issues/verifies state, validates redirect_url domain, issues authentication cookies (SUAT/SURT), and performs a 302 redirect to the final service |
| Auth Server | Converts game tokens to web-only tokens in web_token mode |
| Web Service | Provides service without re-login based on issued authentication cookies |
| Redis | Temporarily stores the mapping between state and game AccessToken for a specific duration (10 minutes) |
Operating Principle
Instead of including the game AccessToken directly in the URL, a one-time state key is issued and passed to the Callback URL. Since the SSO Gateway looks up the actual token using the state to issue authentication cookies, sensitive tokens are not exposed in the URL.
The WebView flow uses the game token directly for web authentication in game_token mode and issues only the access token (SUAT) cookie. The external browser flow uses the web_token mode, where the Auth Server converts the game token into a web token and issues both an access token (SUAT) and a refresh token (SURT).
Integration Guide
Prerequisites
These are common requirements for both the SDK and direct API methods. Detailed preparations for each method are provided in their respective [Development] tracks.
| Item | Description | Note |
|---|---|---|
| Valid Game AccessToken | Possess a valid game AccessToken after completing game login. Login/refresh required if missing or expired |
Required |
| State issuance API access | Obtain the call path for POST /sim/auth/sso/key for the environment-specific domain |
Required |
| WebView cookies/redirects | Configure WebView/browser to store SUAT/SURT cookies and follow 302 redirects | Required |
| redirect_url domain verification | Verify that the final destination address is within the allowed domain range (see Allowed Domains below) | Required |
Consult with the publishing technical lead for API access permissions.
Please confirm environment-specific domains and access permissions through your contact person. Inquiries: sgp_publishtech_d@smilegate.com
Overall SSO Flow
When the game receives the state and calls the Callback, the SSO Gateway validates the domain, retrieves the game token, issues authentication cookies based on the mode, and redirects to the final service. Both the SDK and direct API methods follow the sequence below.
Callback URL Parameters
Construct the Callback URL using the issued state and the final service address. All query parameter values must be URL-encoded.
| Parameter | Required | Default | Description |
|---|---|---|---|
state |
Required | - | A one-time SSO key issued based on the game AccessToken |
redirect_url |
Required | store.onstove.com | The web service address to redirect to after authentication is complete. If not specified, it redirects to the default address |
mode |
Optional | web_token | game_token: Uses the game token as is, issues only SUAT (WebView)web_token: Converts to web token, issues SUAT/SURT (External browser) |
theme |
Optional | light | UI theme to apply to the error screen. light / dark |
game_id |
Optional | Empty string | The game ID that identifies the calling game |
lang |
Optional | ko | Language value to use for error page localization |
Request Format Example
https://api.onstove.com/sim/auth/session/callback
?state={STATE}
&redirect_url={REDIRECT_URL}
&mode={MODE}
&theme={THEME}
&game_id={GAME_ID}
&lang={LANG}
Detailed response code specifications are provided in a separate menu
Detailed specifications, such as response codes per endpoint (e.g., 91030 Wrong Request), will be moved to the API & SDK Reference menu.
redirect_url Allowed Domains
The SSO Gateway validates the domain of the redirect_url to prevent Open Redirect attacks. Since simple string inclusion checks can be bypassed, you must parse the hostname to verify the exact domain or subdomain relationship.
- Allowed domains:
onstove.com,onstove.vn,gate8.vn,gate8.com,ppool.us,gameclub.ph - Whether subdomains are allowed is determined by the Gateway policy
Do not use string inclusion for verificationhttps://onstove.com.attacker.com이나 https://attacker.com/?url=onstove.com Bypass addresses like this can pass through.
Always parse the Hostname to verify the exact domain/subdomain relationship.
Environment Classification Guide
Use domain names specific to each environment. Perform integration tests in the Sandbox first before applying them to Live.
| Environment | Domain Address |
|---|---|
| dev | https://api-dev.onstove.com/sim |
| qa | https://api-qa.onstove.com/sim |
| qa2 | https://api-qa2.onstove.com/sim |
| sandbox | https://api.gate8.com/sim |
| live | https://api.onstove.com/sim |
Development
SDK Integration
This is the basic path for using the Mobile/PC SDK. The SDK handles game AccessToken verification, state issuance, and Callback invocation; the game only needs to specify the execution timing and mode.
Prerequisites
- You must be logged in and possess a valid game AccessToken. If it is missing or expired, perform login/token renewal first.
- Configure the webview to store/maintain authentication cookies (SUAT/SURT) and follow 302 redirects.
- For external browser flows, use
mode=web_tokento issue SUAT and SURT together.
Development Flow
The sequence is identical for both Mobile and PC SDKs; only the method of executing the web service varies by platform.
- Verify the current user's game AccessToken using the SDK login module.
- Pass the game AccessToken to
POST /sim/auth/sso/keyto receive a one-time state. (Valid for 10 minutes) - Create an SSO Callback URL with the state and redirect_url. Specify the mode according to the flow.
- Webview execution:
mode=game_token - External browser execution:
mode=web_token
- Webview execution:
- Use the generated Callback URL as the initial entry address for the webview or default browser.
- The SSO Gateway issues authentication cookies and redirects to the final service via 302.
- Before the game AccessToken expires, receive a new state and repeat the same procedure to automatically renew the authentication cookies.
Do not include authentication tokens directly in the URL
Including the game AccessToken directly in the Callback URL will expose the token. Always pass only the state key in the URL, and do not reuse the existing state during renewal.
Troubleshooting
| Situation | Cause | Action |
|---|---|---|
| Auth state not maintained after Callback | Webview cookie storage disabled or 302 redirect not allowed | Check webview cookie storage and redirect settings. |
| Login cleared in external browser | SURT (refresh token) not issued due to not using web_token | Specify mode=web_token for external browser flows. |
| Redirected to error page due to state error | State not passed, expired (10 min), or already used | Issue the state immediately before execution and use a new state for renewal. |
| Game AccessToken lookup failed | Not logged in or token expired | Re-issue from state after login/token renewal. |
Base_OpenExternalUrl() does not work when running via Steam | Not supported when launched via Steam launcher | Scheduled for support in PCSDK 3.5.1. |
Base_OpenExternalUrl() callback not received | Base_RunCallback() not called in game loop | Call Base_RunCallback() every frame in the game loop. |
| Browser opens but shows as not logged in | SSO processing not applied because it is not a STOVE-related domain | Verify if the address is within the allowed domain scope. |
Sample Code
Mobile SDK External Browser Integration
Open the ONSTOVE page in an external browser while maintaining the authentication session.
Guest accounts cannot use this feature. (Guest users are excluded from support.)
Accounts logged in with a certificate (signature key) different from STOVE do not support SSO external browser calls. Please ensure you have completed 'STOVE Account Conversion' before calling this feature.
/**
* url : "외부브라우저를 연동할 url : 쿠폰 or 커뮤니티 등";
**/
public void OpenExternalUrl()
{
AccessToken accessToken = Auth.AccessToken;
if (accessToken == null)
{
return;
}
string url = "외부브라우저를 연동할 url : 쿠폰 or 커뮤니티 등";
ViewUI.OpenExternalUrl(url, (Result result) =>
{
if (result.IsSuccessful)
{
// 설정된 url 로 외부브라우저 연동 성공
}
else
{
// 외부브라우저 호출 실패
OperationUI.HandleResult(result, (Result operationResult) =>
{
});
}
});
}
PC SDK External Browser Integration
When using the PC SDK, you do not need to implement steps 1–5 above if you are opening web services in an external browser.Base_OpenExternalUrl() By calling this once, the SDK handles game AccessToken verification, state issuance, SSO Callback invocation, and external browser execution. Users can access web services like the Stove community or customer support in their default browser without re-logging in.
The flow for opening in a webview within the game screen (mode=game_token) is not covered by this API. Games that use the webview flow or do not use the PC SDK must implement steps 1–5 above as they are.
| Item | Description |
|---|---|
| Provided Module | BaseSDK. Independent of the popup (ViewSDK) module and can be called without ViewSDK initialization. |
| Application Flow | External browser flow. Launches the user's default browser. |
| State/Mode Specification | Handled internally by the SDK. The game only needs to pass the URL to open. |
| SSO Coverage | SSO processing is applied only when opening Stove-related domains. Other addresses are opened in the browser without authentication. |
| Call Condition | Base_Initialize() Call after completion. |
| Result Reception | Asynchronous API. Results are delivered as a callback on the thread that called Base_RunCallback(). |
Cannot be used when launched via the Steam launcher
This feature will be provided in PCSDK 3.5.1.
void Base_OpenExternalUrl(const wchar_t* url, OnOpenExternalUrlFinished onFinished);
// 콜백
typedef void(__cdecl* OnOpenExternalUrlFinished)(CallbackResult callbackResult);
| Parameters | Required | Description |
|---|---|---|
url |
Required | The web service address to open. SSO processing is applied if it is a Stove-related domain. |
onFinished |
Optional | Callback to receive execution results. Can be omitted if result verification is not required. |
#include "BaseSDK.h"
using namespace Stove::PCSDK::Base;
// Base_Initialize() 성공 이후에 호출해요.
void OpenStoveCommunity()
{
Base_OpenExternalUrl(L"https://www.onstove.com", [](CallbackResult callbackResult)
{
if (callbackResult.GetResult().IsSuccessful())
{
// 브라우저 실행 성공 시 로직을 구현해 주세요.
}
else
{
// 실패 시 로직을 구현해 주세요.
}
});
}
// 게임 루프에서 매 프레임 호출해야 콜백이 전달돼요.
void GameLoop()
{
Base_RunCallback();
}
Callback success means the browser has been launched
The success of the callback only indicates that the external browser has been opened. Authentication results occurring within the web service after the browser is opened are not delivered via callback.
Direct API Integration
This is an exception path where games not using the SDK call the SSO API directly. The game must implement state issuance/storage, token lookup/cookie setting, and token renewal itself.
Prerequisites
- The Game Client must hold a valid game AccessToken and track/manage its expiration time.
- The SSO Gateway must be integrated with Redis for state-based token storage and retrieval.
- The SSO Gateway must have a redirect_url allowed domain validation policy configured.
- When using web_token, the Auth Server must provide an API to convert game tokens into web tokens.
- The Web Service must support SUAT/SURT cookie-based authentication.
- The Game Client must implement an automatic renewal feature before the game token expires.
Development Flow
The Game Client receives a state using the game AccessToken. The Sim API generates a random state, maps and stores it with the game AccessToken in Redis, and returns the state.
- Request:
POST /sim/auth/sso/key(Headeraccess_token: {Game AccessToken},Content-Type: application/json) - Successful Response:
{ "code": 0, "message": "ok", "value": "{state}" } - The state is valid for 10 minutes and is one-time use.
When the Callback is called with the issued state, the Sim API validates the redirect_url domain and retrieves the game AccessToken from Redis. Depending on the mode, it issues an authentication cookie and then performs a 302 redirect to the final service.
This is the processing sequence of the Sim API.
- Check if the state exists
- Validate redirect_url format and allowed domain
- Retrieve the game AccessToken linked to the state from Redis
- Delete the used state (mark as non-reusable)
- Determine the authentication token based on the mode
- Set the authentication cookie in the HTTP response
- 302 Redirect to the validated redirect_url
The entity responsible for renewing the authentication token is the game or app (Game Client). If you receive a new state before the game AccessToken expires and repeat the same procedure, the authentication cookie will be automatically renewed.
| Item | Value |
|---|---|
| Renewal Entity | Game or App (SDK) managing the authentication token |
| Expiration Time | 6 hours |
| Renewal Timing | 10-30 minutes before expiration |
| Action after renewal | Re-issue the state key and proceed with the SSO process again |
Do not reuse the existing state during renewal
You must always issue a new state even when renewing. Authentication will fail with an expired or used state.
In environments that do not support automatic renewal, handle it as follows:
If a user action occurs within the WebView content while the token is expired, display a token expiration notice and either close the WebView or proceed with re-authentication.
The handling method for token expiration may vary depending on service requirements. Please implement it after prior consultation with the service developer.
Troubleshooting
If an error occurs during SSO processing, you will be redirected to a common error page. Here are the main error situations and handling measures.
| Error Situation | Gateway Handling | Handling Measure |
|---|---|---|
| State missing / expired / reused | Request denied / Auth failed | Re-issue a new state and retry. |
| redirect_url missing / unauthorized domain | Request denied | Correct the address to be within the allowed domain range. |
| mode error | Request denied or default applied | Specify either game_token or web_token according to the flow. |
| Game token retrieval failed | Auth failed | Log in/renew token and re-issue from the state. |
| Web token conversion failed | Temporary error | Retry, and if it persists, contact the person in charge. |
| Cookie issuance failed / 302 Redirect failed | Auth failed / Error page | Check the WebView/browser's cookie storage and redirect settings. |
Since errors are handled outside the WebView, the parameter configuration when moving to the error page varies depending on the mode.
- game_token: Moves in
https://oops.onstove.com/main?theme={theme}&lang={lang}로. Since it is handled within the WebView, the redirect_url query parameter is not required. - web_token: Moves in
https://oops.onstove.com/main?redirect_url={redirect_url}&theme={theme}&lang={lang}으로 via an external browser. If a redirect_url is provided, it moves to that address; if not, it moves tohttps://store.onstove.com/{lang}으로.
Sample Code
PC Client (Game/App)
This is the standardized Callback URL builder and execution code. Please apply the actual WebView and external browser execution method names for the Native App (hereinafter App) according to the App specification.
// SsoUrl.h
#pragma once
#include <string>
#include <sstream>
#include <stdexcept>
namespace sso {
// RFC 3986 unreserved 외 문자를 %XX로 인코딩
inline std::string UrlEncode(const std::string& value) {
static const char hex[] = "0123456789ABCDEF";
std::string out; out.reserve(value.size() * 3);
for (unsigned char c : value) {
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
c == '-' || c == '_' || c == '.' || c == '~') {
out.push_back(static_cast<char>(c));
} else {
out.push_back('%');
out.push_back(hex[c >> 4]);
out.push_back(hex[c & 0x0F]);
}
}
return out;
}
struct SsoParams {
std::string state; // Required
std::string redirectUrl; // Required
std::string mode = "game_token"; // game_token | web_token
std::string theme = "light"; // light | dark
std::string gameId;
std::string lang = "ko";
};
inline std::string BuildSsoCallbackUrl(const SsoParams& p) {
static const char* kBaseUrl =
"https://api.onstove.com/sim/auth/session/callback";
if (p.state.empty()) throw std::invalid_argument("state is required.");
if (p.redirectUrl.empty()) throw std::invalid_argument("redirect_url is required.");
std::ostringstream url;
url << kBaseUrl
<< "?state=" << UrlEncode(p.state)
<< "&redirect_url=" << UrlEncode(p.redirectUrl)
<< "&mode=" << UrlEncode(p.mode.empty() ? "game_token" : p.mode)
<< "&theme=" << UrlEncode(p.theme.empty() ? "light" : p.theme)
<< "&lang=" << UrlEncode(p.lang.empty() ? "ko" : p.lang);
if (!p.gameId.empty()) url << "&game_id=" << UrlEncode(p.gameId);
return url.str();
}
} // namespace sso
#include "include/cef_browser.h"
#include "include/cef_frame.h"
#include "SsoUrl.h"
void NavigateSso(CefRefPtr<CefBrowser> browser, const sso::SsoParams& baseParams) {
if (!browser) return;
sso::SsoParams params = baseParams;
params.mode = "game_token"; // 웹뷰는 game_token
const std::string url = sso::BuildSsoCallbackUrl(params);
browser->GetMainFrame()->LoadURL(CefString(url)); // CefString은 UTF-8 처리
}
using UnityEngine;
public static class SsoUnityBrowser
{
// 외부 브라우저는 web_token (SUAT + SURT)
public static void OpenExternal(
string state, string redirectUrl, string gameId, string theme = "light")
{
string url = SsoUrlBuilder.Build(
state, redirectUrl, "web_token", theme, gameId, "ko");
Application.OpenURL(url);
}
}
Mobile Client (게임/앱)
표준화된 Callback URL 빌더와 실행 코드입니다. Mobile 게임/앱의 실제 WebView 및 외부 브라우저 실행 메서드는 각 App의 명세에 맞게 적용해 주세요.
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public static class SsoUtil
{
private const string CALLBACK_URL =
"https://api.onstove.com/sim/auth/session/callback";
public static string CreateMobileSsoUrl(
string state,
string redirectUrl,
string mode = "game_token",
string theme = "light",
string gameId = "",
string lang = "ko")
{
if (string.IsNullOrWhiteSpace(state))
{
throw new ArgumentException("state is required.", nameof(state));
}
if (string.IsNullOrWhiteSpace(redirectUrl))
{
throw new ArgumentException(
"redirectUrl is required.",
nameof(redirectUrl)
);
}
var queryParams = new List<string>
{
$"state={UnityWebRequest.EscapeURL(state)}",
$"redirect_url={UnityWebRequest.EscapeURL(redirectUrl)}",
$"mode={UnityWebRequest.EscapeURL(mode)}",
$"theme={UnityWebRequest.EscapeURL(theme)}",
$"lang={UnityWebRequest.EscapeURL(lang)}"
};
if (!string.IsNullOrWhiteSpace(gameId))
{
queryParams.Add(
$"game_id={UnityWebRequest.EscapeURL(gameId)}"
);
}
return $"{CALLBACK_URL}?{string.Join("&", queryParams)}";
}
}
// URL 생성 예제
string ssoUrl = SsoUtil.CreateMobileSsoUrl(
state: state,
redirectUrl: "https://service.onstove.com/main",
gameId: "STOVE_LORD"
);
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class SsoWebView {
public static void openWebView(
WebView webView,
String ssoCallbackUrl
) {
if (webView == null) {
throw new IllegalArgumentException("webView is required.");
}
if (ssoCallbackUrl == null || ssoCallbackUrl.isEmpty()) {
throw new IllegalArgumentException("ssoCallbackUrl is required.");
}
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
webView.setWebViewClient(new WebViewClient());
// SSO Callback URL을 최초 진입 주소로 사용
webView.loadUrl(ssoCallbackUrl);
}
}
// 사용 예제
String ssoUrl = SsoUtil.createMobileSsoUrl(
state,
"https://service.onstove.com/main",
"game_token",
"light",
"STOVE_LORD",
"ko"
);
SsoWebView.openWebView(webView, ssoUrl);
import android.content.Context
import android.content.Intent
import android.net.Uri
object SsoBrowser {
fun openExternalBrowser(
context: Context,
ssoCallbackUrl: String
) {
require(ssoCallbackUrl.isNotBlank()) {
"ssoCallbackUrl is required."
}
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse(ssoCallbackUrl)
).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
}
}
// 사용 예제
val ssoUrl = SsoUtil.createMobileSsoUrl(
state = state,
redirectUrl = "https://service.onstove.com/main",
mode = "web_token", // 외부 브라우저는 web_token 권장
gameId = "STOVE_LORD"
)
SsoBrowser.openExternalBrowser(
context = this,
ssoCallbackUrl = ssoUrl
)
Web Client (Web Service/WIC)
This is a JavaScript example of how to open web content links in a WebView, either internally or in an external browser, based on the specified Target value.
| Value | Description |
|---|---|
| INTERNAL_BROWSER | Open in the internal (current WebView) browser |
| EXTERNAL_BROWSER | Open in the OS default external browser |
The Target option value must be implemented separately in the SDK or Game Client.INTERNAL_BROWSER and EXTERNAL_BROWSER are custom Target values defined by the SDK.
Therefore, the SDK or Game Client must be pre-integrated to recognize these values and handle the connection to either the internal or external browser for it to function correctly.
window.open() This is an example of using the method to launch a new WebView.
target If you set the value to INTERNAL_BROWSER, a new WebView will be created within the game.
Since the newly created WebView shares the authentication cookies of the parent WebView, you can use web services while maintaining your login status without having to perform the SSO authentication process again.
window.open() This is an example of using the method to execute the SSO Callback URL in the OS default external browser.
target If you set the value to EXTERNAL_BROWSER, the SDK or Game Client will launch the OS default web browser to call the SSO Callback URL.
The SSO Gateway verifies the received State, issues a web authentication cookie, and then redirects to redirect_url so that you can use the web service in a logged-in state.
js function openExternalBrowser({ state, redirectUrl, mode = "web_token", theme = "light", game_id = "", lang = "ko" }) { const url = new URL("/sim/auth/session/callback", window.location.origin); url.searchParams.set("state", state); url.searchParams.set("redirect_url", redirectUrl); url.searchParams.set("mode", mode); url.searchParams.set("theme", theme); url.searchParams.set("lang", lang);
if (game_id) { url.searchParams.set("game_id", game_id); }
window.open(url.toString(), "EXTERNAL_BROWSER", "noopener,noreferrer"); // New window }
openExternalBrowser({ state: "issued-state", redirectUrl: "https://service.onstove.com/main" });
:::
:::dc-tab-item{label="웹뷰 (web_token)"}
```js
function openInternalBrowser({
state, redirectUrl,
mode = "web_token", theme = "light", game_id = "", lang = "ko"
}) {
const url = new URL("/sim/auth/session/callback", window.location.origin);
url.searchParams.set("state", state);
url.searchParams.set("redirect_url", redirectUrl);
url.searchParams.set("mode", mode);
url.searchParams.set("theme", theme);
url.searchParams.set("lang", lang);
if (game_id) {
url.searchParams.set("game_id", game_id);
}
window.open(url.toString(), "INTERNAL_BROWSER", "noopener,noreferrer"); // New window
}
openInternalBrowser({
state: "issued-state",
redirectUrl: "https://service.onstove.com/main"
});