— BROWSER (JS) + UNITY (WebGL / STANDALONE) CLIENT SDK —
A client-side SDK guide for integrating the AltsCodex OAuth login flow. The JavaScript SDK covers browser environments; the Unity SDK targets WebGL and Standalone (Windows / macOS / Linux) game clients. Both never see the client_secret — they only return a short-lived JWT that your game backend exchanges via the server SDKs.
AltsCodex OAuth 로그인 흐름을 클라이언트 측에서 연동하는 가이드입니다. JavaScript SDK 는 브라우저 환경, Unity SDK 는 WebGL 및 Standalone(Windows / macOS / Linux) 게임 클라이언트를 다룹니다. 두 SDK 모두 client_secret 을 보지 않고, 짧은 수명의 JWT 만 반환합니다 — 이를 게임 백엔드로 보내 서버 SDK 로 교환합니다.
Integrate the AltsCodex frontend SDK in 4 steps. 4단계로 AltsCodex 프론트엔드 SDK를 연동하세요.
Rather than wiring the SDK by hand, you can hand a single copy-paste prompt to an LLM coding agent (Claude Code, Cursor, etc.) and have it complete the AltsCodex SSO integration in one pass. The prompt below already bakes in the pitfalls we hit during a real integration, so the agent avoids them up front instead of discovering them by trial and error. SDK 를 손으로 배선하는 대신, LLM 코딩 에이전트(Claude Code, Cursor 등)에 아래 프롬프트를 복사-붙여넣기로 넘겨 AltsCodex SSO 연동을 한 번에 끝낼 수 있습니다. 실제 연동에서 부딪힌 함정들이 프롬프트에 미리 반영돼 있어, 에이전트가 시행착오로 발견하기 전에 처음부터 피해 갑니다.
The agent cannot register your app or read your dashboard. Finish these at the Developer Center before pasting the prompt: 에이전트는 앱 등록이나 대시보드 확인을 대신 못 합니다. 프롬프트를 붙여넣기 전에 개발자 센터에서 아래를 끝내세요.
redirect_uri mismatch.
앱을 등록하고 Redirect URI 를 앱이 보낼 값과 정확히 같은 문자열로 지정하세요 — 스킴·호스트·포트·경로가 같아야 하고 후행 슬래시 금지. 한 글자만 달라도 redirect_uri mismatch 가 납니다.
client_id and client_secret. The secret goes into a server-side environment variable only — never a frontend bundle.
발급된 client_id 와 client_secret 을 확보하세요. secret 은 서버 환경변수로만 두고 프론트엔드 번들에 절대 넣지 마세요.
Paste this as-is, then replace the placeholder values (or let the agent ask you for them). It is stack-agnostic — it states the contract and the traps, not a specific framework. 아래를 그대로 붙여넣고 플레이스홀더 값만 교체하세요(또는 에이전트가 물어보게 두세요). 특정 프레임워크가 아니라 계약과 함정만 서술하므로 스택 무관하게 동작합니다.
[역할] 너는 AltsCodex(DeOAuth) SSO 를 우리 앱에 연동하는 시니어 개발자다.
아래 계약과 함정을 그대로 지켜, 팝업 로그인 → 슬롯(부계정) 정보 조회까지
한 번에 동작하게 구현하라. 임의 추측 금지 — 모르는 값은 나에게 먼저 물어라.
[전제] 개발자센터에 앱을 이미 등록했고 아래 값을 가지고 있다.
- AUTH_SERVER_URL (예: https://api.altscodex.com)
- CLIENT_ID
- CLIENT_SECRET (서버 환경변수로만 사용, 프론트 번들 금지)
- REDIRECT_URI (개발자센터 등록값과 문자 단위로 정확히 일치, 후행 슬래시 금지)
[프론트엔드 계약]
- @altscodex/sdk 의 login() 팝업 방식만 쓴다. authorize URL 을 손으로 만들지 마라.
- 서버가 /sdk.js 와 /config 를 서빙하고(공개 안전값 authServerUrl·clientId·redirectUri
만 노출), 프론트는 이를 로드해 new AltsCodex(config).login() 을 호출한다.
- login() 성공 시 받은 JWT 를 우리 백엔드로 POST 한다.
[백엔드 계약]
- new AltsCodexBackend({ authServerUrl, clientId, clientSecret, redirectUri })
- 콜백 처리: handleCallback(req, res)
- 슬롯 조회: getSlotInfo(jwt)
- 콜백 라우트와 JWT→세션 교환 라우트는 반드시 같은 backend 인스턴스를 공유한다.
(pending state 매칭이 그 인스턴스의 메모리에 있다.)
[반드시 지킬 함정]
1. (최중요) authorize 콜백은 브라우저 리다이렉트가 아니라 서버→서버 POST 로 온다
(파라미터는 쿼리스트링). 콜백 라우트를 GET·POST 겸용(all 메서드)으로 열어라.
증상이 "Cannot POST /auth/callback" 또는 "authorize callback timeout" 이면 이 문제다.
2. 브라우저 콘솔의 "Cross-Origin-Opener-Policy policy would block the window.closed
call" 경고는 무해하다. SDK 가 COOP 내성 폴링을 내장한다. 이 경고를 버그로 보고
COOP 헤더를 만지지 마라. 성공 판정은 오직 postMessage 수신 여부다.
3. 로그인 URL 은 client_id · redirect_uri · response_type · state 4개가 모두 필요하다.
SDK login() 이 자동 구성하므로 직접 만들지 마라. 누락하면 ERR_MISSING_PARAMS 가 난다.
4. REDIRECT_URI 는 개발자센터 등록값과 정확히 일치해야 한다(스킴·호스트·포트·경로·슬래시).
[선택 — 에이전트 위임 토큰까지 받는 경우]
- verifyAgentToken 은 별도의 backend 인스턴스로 만든다.
이 인스턴스의 authServerUrl 은 델리게이터(https://delegator.altscodex.com)로 지정하라.
JWKS 발급처가 SSO 서버와 다르다. 상세는 "에이전트 위임 가이드" 문서를 따르라.
[완료 기준]
- 팝업 로그인 → 콜백 POST 수신 → 세션 확립 → getSlotInfo 성공까지 실제로 동작한다.
- 서버 로그에 "authorize callback timeout" 이 없다.
- 위 기준을 한 번 실행해 확인한 뒤 완료라고 보고하라.
[참고 문서]
- 프론트엔드: https://developers.altscodex.com/sdk_frontend
- 백엔드: https://developers.altscodex.com/sdk_backend
verifyAgentToken flow — JWKS caching, audience, scope gating, and high-risk introspect — follow the Accepting Agent Delegation guide. Its authServerUrl points at the delegation server (https://delegator.altscodex.com), a different JWKS issuer than the SSO server, so keep it on a separate backend instance.
위 선택 블록은 개요만 담습니다. verifyAgentToken 전체 흐름(JWKS 캐싱·audience·스코프 게이트·고위험 introspect)은 에이전트 위임 수용 가이드를 따르세요. 이 가이드의 authServerUrl 은 위임 서버(https://delegator.altscodex.com)를 가리키며 SSO 서버와 JWKS 발급처가 다르므로, 반드시 별도의 backend 인스턴스로 분리하세요.
If the agent (or you) gets stuck, match the symptom here before changing anything: 에이전트(또는 본인)가 막히면 무언가 바꾸기 전에 아래에서 증상을 먼저 대조하세요.
| Symptom 증상 | Cause 원인 | Fix 조치 |
|---|---|---|
| Cannot POST /auth/callback / authorize callback timeout |
Callback route is GET-only, but the server posts server-to-server 콜백 라우트가 GET 전용인데 서버가 서버→서버 POST 로 보냄 | Open the callback route for all methods (GET + POST) 콜백 라우트를 all 메서드(GET·POST 겸용)로 열기 |
| Cross-Origin-Opener-Policy … window.closed console warning 콘솔 경고 | Harmless — SDK has a COOP-tolerant poll built in 무해 — SDK 에 COOP 내성 폴링 내장 | Ignore it. Judge success by postMessage receipt, not this warning 무시. 성공 판정은 이 경고가 아니라 postMessage 수신으로 |
| ERR_MISSING_PARAMS | A hand-built login URL dropped one of client_id / redirect_uri / response_type / state 직접 만든 로그인 URL 에서 client_id / redirect_uri / response_type / state 중 하나 누락 |
Use SDK login() — it builds all four automatically
SDK login() 사용 — 4개를 자동 구성함
|
| Popup never closes / no response 팝업이 안 닫힘 / 응답 없음 | redirectUri does not match the registered value redirectUri 가 등록값과 불일치 | Compare against the Developer Center value character by character (incl. trailing slash) 개발자센터 등록값과 문자 단위로 대조(후행 슬래시 포함) |
Install the SDK using npm. npm을 사용하여 SDK를 설치합니다.
npm install @altscodex/sdk
Choose your import method: Import 방법을 선택하세요:
import AltsCodex from '@altscodex/sdk';
const AltsCodex = require('@altscodex/sdk');
Create a AltsCodex instance using the client credentials issued from the developer center. AltsCodex 인스턴스를 생성합니다. 개발자 센터에서 발급받은 클라이언트 정보를 사용하세요.
| Parameter | Type | Required | Description |
|---|---|---|---|
| altscodexUrl | string | Required | AltsCodex server base URL AltsCodex 서버 base URL |
| clientId | string | Required | Client ID issued from the developer center 개발자센터에서 발급받은 클라이언트 ID |
| redirectUri | string | Required | URI to redirect to after OAuth authorization OAuth 인가 후 리다이렉트될 URI |
| responseType | string | Optional | OAuth response type (default: 'code') OAuth response type (default: 'code') |
| popupWidth | number | Optional | Popup width (default: 600) 팝업 너비 (default: 600) |
| popupHeight | number | Optional | Popup height (default: 500) 팝업 높이 (default: 500) |
const sdk = new AltsCodex({
altscodexUrl: 'https://your-altscodex.com',
clientId: 'your-client-id',
redirectUri: 'https://your-app.com/callback',
});
sdk.login(options?) —
Opens an OAuth popup and returns the login result as a Promise.
OAuth 팝업을 열고 로그인 결과를 Promise로 반환합니다.
{ from: "altscodex", type: "success"|"fail", data: JWT|ERROR_MSG } is sent to the parent window. The SDK handles this automatically.
팝업 창에서 로그인이 완료되면 { from: "altscodex", type: "success"|"fail", data: JWT|ERROR_MSG } 형식의 postMessage를 부모 창으로 전송합니다. SDK가 이를 자동으로 처리합니다.
| Parameter | Type | Required | Description |
|---|---|---|---|
| state | string | Optional | CSRF state value (auto-generated if omitted) CSRF 방지용 state 값 (미입력 시 자동 생성) |
| timeout | number | Optional | Login timeout in ms (default: 120000) 로그인 타임아웃 ms (default: 120000) |
try {
const { jwt } = await sdk.login({ state: String(Date.now()) });
console.log('JWT:', jwt);
// JWT를 게임 백엔드로 전달
await fetch('/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ jwt }),
});
} catch (err) {
console.error('Login failed:', err.message);
}
Manage tokens and handle logout after login. 로그인 후 토큰을 관리하고 로그아웃을 처리합니다.
isLoggedIn() now decodes the JWT exp claim — an expired token returns false
(user JWTs expire after 7 days), so your UI can prompt re-login instead of showing a broken logged-in state.
② logout() never throws — local tokens are always cleared even if the server or network fails.
Logging out matters: a slot (alt account) can be switched to sleep mode — and another alt activated — only after logout.
③ refresh() is deprecated and will be removed in v4.0.0 — refresh tokens server-side instead.
① isLoggedIn() 이 JWT exp(만료)를 검사합니다 — 만료 토큰은 false
(사용자 JWT 는 7일 만료). 만료 후 "로그인된 듯 보이는데 호출은 전부 실패"하는 상태 대신 재로그인을 유도할 수 있습니다.
② logout() 은 더 이상 throw 하지 않습니다 — 서버/네트워크 실패 시에도 로컬 토큰 정리가 항상 보장됩니다.
로그아웃이 되어야 부계정(slot)을 sleep 모드로 변경할 수 있고, 다른 부계정을 활성화할 수 있으므로 로그아웃 처리가 중요합니다.
③ refresh() 는 deprecated 되었으며 v4.0.0 에서 제거 예정입니다 — 토큰 갱신은 서버측에서 수행하세요.
| Method | Returns | Description |
|---|---|---|
| sdk.logout(options?) | Promise<void> | Deactivates De-OAuth server JWT + clears localStorage. Never throws (v3) — local cleanup is guaranteed even on server failure. Required before switching a slot to sleep mode / activating another alt De-OAuth 서버 JWT 비활성화 + localStorage 정리. v3 부터 throw 하지 않음 — 서버 실패에도 로컬 정리 보장. 부계정 sleep 전환·다른 부계정 활성화 전에 필수 |
| sdk.getToken() | string | null | Returns stored access_token 저장된 access_token 반환 |
| sdk.getRefreshToken() | string | null | Returns stored refresh_token 저장된 refresh_token 반환 |
| sdk.getCode() | string | null | Returns stored OAuth code 저장된 OAuth code 반환 |
| sdk.isLoggedIn() | boolean | Whether a non-expired access_token exists (v3: decodes JWT exp; expired → false) 미만료 access_token 보유 여부 (v3: JWT exp 검사, 만료 시 false) |
| sdk.refresh(options) | Promise<{...}> |
|
// 로그아웃
await sdk.logout();
// 토큰 확인
if (sdk.isLoggedIn()) {
const token = sdk.getToken();
console.log('Token:', token);
}
login() flow never stores a refresh_token, so this method always fails in the official flow — and requiring clientSecret in browser code leaks your secret to anyone. Calling it now logs a deprecation warning; it will be removed in v4.0.0. Refresh tokens server-side instead (Go SDK: Backend.RefreshTokens(); JS/Python backend equivalents planned).
popup login() 플로우는 refresh_token 을 저장하지 않으므로 이 메서드는 공식 플로우에서 항상 실패하며, clientSecret 을 브라우저 코드에 요구해 시크릿이 누구에게나 노출됩니다. 호출 시 deprecation 경고가 출력되고 v4.0.0 에서 제거됩니다. 토큰 갱신은 서버측에서 수행하세요 (Go SDK: Backend.RefreshTokens(), JS/Python 백엔드 SDK 는 동등 API 추가 예정).
Error messages and their causes from the SDK. SDK에서 발생할 수 있는 에러 메시지와 원인입니다.
| Error Message | Cause |
|---|---|
| Popup blocked | Browser popup blocked 브라우저 팝업 차단 설정 |
| User closed the login window | User closed the popup window 사용자가 팝업 창을 닫음 |
| Login failed: {reason} | Error during login processing 로그인 처리 중 오류 발생 |
| Login timeout | No response within timeout (default 120s) timeout 시간 내 응답 없음 (default 120초) |
try {
const { jwt } = await sdk.login();
} catch (err) {
if (err.message.includes('Popup blocked')) {
alert('팝업을 허용해주세요.');
} else if (err.message.includes('User closed')) {
console.log('사용자가 로그인을 취소했습니다.');
} else if (err.message.includes('timeout')) {
console.error('로그인 시간이 초과되었습니다.');
} else {
console.error('로그인 오류:', err.message);
}
}
The SDK does not read process.env or import.meta.env itself — it accepts plain options. Inject them from your build environment so secrets stay out of source code.
SDK 자체는 process.env / import.meta.env 를 읽지 않습니다. 옵션으로 직접 전달받습니다. 빌드 환경에서 .env 로 주입해 시크릿이 소스코드에 남지 않도록 하세요.
# .env.production
VITE_ALTSCODEX_URL=https://altscodex.com
VITE_CLIENT_ID=your-registered-client-id
VITE_REDIRECT_URI=https://yourapp.com/callback
// AuthContext.tsx
import AltsCodex from '@altscodex/sdk';
const sdk = new AltsCodex({
altscodexUrl: import.meta.env.VITE_ALTSCODEX_URL,
clientId: import.meta.env.VITE_CLIENT_ID,
redirectUri: import.meta.env.VITE_REDIRECT_URI,
});
# .env.local (server side)
ALTSCODEX_AUTH_SERVER_URL=https://api.altscodex.com
ALTSCODEX_CLIENT_ID=your-registered-client-id
ALTSCODEX_CLIENT_SECRET=your-client-secret # NEVER expose to the browser
ALTSCODEX_REDIRECT_URI=https://yourapp.com/getinfo
Use a separate .env.<mode> file per environment (local / staging / production). Vite picks the file by --mode; Next.js picks by NODE_ENV.
환경별로 .env.<mode> 파일을 분리하세요. Vite 는 --mode 로, Next.js 는 NODE_ENV 로 파일을 선택합니다.
| Purpose | Production | Local development |
|---|---|---|
| Frontend | https://altscodex.com (or www.) |
http://localhost:3000 |
| Backend / API | https://api.altscodex.com |
http://localhost:3000 |
| Developer Center | https://developers.altscodex.com |
— |
Do NOT invent subdomains like login.altscodex.com, oauth.altscodex.com, auth.altscodex.com. They resolve to NXDOMAIN and the popup silently fails with User closed the login window after a long timeout.
login.altscodex.com, oauth.altscodex.com, auth.altscodex.com 같은 미등록 서브도메인을 임의로 사용하지 마세요. NXDOMAIN 이 떨어지고 팝업이 한참 후 User closed the login window 로 조용히 실패합니다.
clientId and redirectUri first
2. clientId / redirectUri 사전 등록 필수
Register your application here at the Developer Center to obtain a clientId and clientSecret. Hard-coding values that are not registered (or a redirectUri that does not exactly match the registered value — including trailing slash) results in invalid_client / redirect_uri mismatch 401 errors.
개발자 센터에서 애플리케이션을 등록해 clientId / clientSecret 을 발급받으세요. 미등록 값을 박거나 redirectUri 가 등록된 값과 정확히 일치하지 않으면 (트레일링 슬래시 포함) invalid_client / redirect_uri mismatch 401 오류가 발생합니다.
@webxcom/sdk v1.x
3. @webxcom/sdk v1.x 에서 마이그레이션
Old (@webxcom/sdk v1.x) |
New (@altscodex/sdk v2.x) |
|---|---|
import WebXCOM from '@webxcom/sdk' |
import AltsCodex from '@altscodex/sdk' |
new WebXCOM({ webxcomUrl: ... }) |
new AltsCodex({ altscodexUrl: ... }) |
WebXCOMBackend |
AltsCodexBackend |
webxcom_* localStorage keys |
altscodex_* localStorage keys |
Coexistence: the platform server emits postMessage in dual-broadcast mode — one with from: "altscodex" (consumed by v2.x SDK) and a second with from: "webxcom" (consumed by v1.x SDK). Existing v1.x apps continue to work unchanged during gradual migration.
양립 호환: 플랫폼 서버는 dual-broadcast 로 동작합니다. from: "altscodex" (v2.x SDK 수신) 과 from: "webxcom" (v1.x SDK 수신) 두 메시지를 동시에 발송합니다. 옛 v1.x 앱은 코드 변경 없이 동작합니다.
If your hosting page sets Cross-Origin-Opener-Policy: same-origin, the browser may block the SDK's popup.closed poll. The SDK degrades gracefully (stops polling and relies on postMessage + timeout), but a user closing the popup with the X button may not be detected. Recommended COOP for OAuth opener pages: same-origin-allow-popups or unsafe-none.
호스팅 페이지가 Cross-Origin-Opener-Policy: same-origin 을 보내면 SDK 의 popup.closed 폴링이 차단될 수 있습니다. SDK 는 graceful 하게 폴링을 중단하고 postMessage + timeout 으로 종료를 결정하지만, 사용자가 X 로 닫는 케이스는 감지 못할 수 있습니다. OAuth 팝업을 여는 페이지의 권장 COOP 정책: same-origin-allow-popups 또는 unsafe-none.
If your client is a Unity game, use the Unity SDK instead of the browser JavaScript SDK. It mirrors the same login flow (popup + JWT) but exposes a single async C# API: await AltsCodexLogin.LoginAsync(config).
클라이언트가 Unity 게임이라면 브라우저 JavaScript SDK 대신 Unity SDK 를 사용하세요. 동일한 로그인 흐름(popup + JWT)을 그대로 재현하되 비동기 C# API 하나로 노출합니다: await AltsCodexLogin.LoginAsync(config).
| Target | Status (0.1.x) | Mechanism |
|---|---|---|
WebGL |
✅ Supported | window.open popup + postMessage (JS SDK 와 동일 프로토콜) |
Standalone (Win / macOS / Linux) |
✅ Supported | Application.OpenURL + loopback HttpListener (RFC 8252) |
| Unity Editor (Play mode) | ✅ Supported | Standalone 경로 재사용 |
| iOS / Android / 기타 | ❌ Out of scope | OS 네이티브 플러그인 필요 — 0.2.x 로 연기 |
Open Window → Package Manager → + → Add package from git URL... and paste: Window → Package Manager → + → Add package from git URL... 에서 다음을 입력하세요:
https://github.com/alts-codex/unity-sdk.git#v0.1.0
Or edit Packages/manifest.json directly:
또는 Packages/manifest.json 을 직접 편집:
{
"dependencies": {
"com.alts-codex.auth-sdk": "https://github.com/alts-codex/unity-sdk.git#v0.1.0"
}
}
using System.Threading;
using UnityEngine;
using AltsCodex;
public class LoginButton : MonoBehaviour
{
public async void OnClick()
{
var config = new LoginConfig
{
ClientId = "YOUR_CLIENT_ID", // Developer Center 발급
RedirectUri = "https://yourgame.example.com/callback",
// AltsCodexUrl 기본값: https://altscodex.com
};
var result = await AltsCodexLogin.LoginAsync(config);
if (!result.Success)
{
Debug.LogWarning("login failed: " + result.ErrorMessage);
return;
}
// result.Jwt 를 게임 백엔드로 전송 → 백엔드가 Node/Python/Go SDK 로 슬롯 정보 조회
Debug.Log("JWT received, length=" + result.Jwt.Length);
}
}
LoginAsync calls into a bundled .jslib, which opens window.open(loginUrl) against altscodex.com. The popup posts back via postMessage and the bridge forwards the payload to C# through unityInstance.SendMessage(...). Your WebGL template must expose the instance: window.unityInstance = unityInstance; after createUnityInstance(...).
C# LoginAsync 가 번들된 .jslib 를 호출해 altscodex.com 으로 window.open(loginUrl). popup 이 postMessage 로 응답하면 브릿지가 unityInstance.SendMessage(...) 로 C# 에 전달합니다. WebGL 템플릿에서 반드시 인스턴스를 노출해야 합니다: createUnityInstance(...) 호출 후 window.unityInstance = unityInstance;.
http://127.0.0.1:<port>/callback via System.Net.HttpListener (ephemeral port by default, or pin via LoopbackPort), opens the OS default browser with Application.OpenURL, then waits for the platform server to redirect back with jwt + state query parameters. The Developer Center must whitelist this loopback URI (fixed port like http://127.0.0.1:17070/callback, or wildcard http://127.0.0.1:*/callback if supported).
SDK 가 System.Net.HttpListener 로 http://127.0.0.1:<port>/callback 바인딩(기본 ephemeral, LoopbackPort 로 고정 가능), Application.OpenURL 로 OS 기본 브라우저 열기, 플랫폼 서버가 jwt + state 쿼리 파라미터로 리다이렉트 대기. Developer Center 에 이 loopback URI 를 등록해야 합니다(http://127.0.0.1:17070/callback 고정 포트, 또는 지원 시 http://127.0.0.1:*/callback 와일드카드).
il2cpp_dumper / dnSpy. The Unity SDK intentionally does not accept clientSecret — only clientId + redirectUri. Send the resulting JWT to your game backend; the backend uses the Node / Python / Go server SDK with clientSecret to call get_slot_info(jwt).
Unity 빌드는 il2cpp_dumper / dnSpy 로 1분이면 디컴파일됩니다. Unity SDK 는 의도적으로 clientSecret 을 받지 않습니다 — clientId + redirectUri 만 받습니다. 받은 JWT 를 게임 백엔드로 전송하고, 백엔드가 clientSecret 을 가진 Node / Python / Go 서버 SDK 로 get_slot_info(jwt) 를 호출하세요.
alts-codex/unity-sdk —
source, full README, issues
소스, 전체 README, 이슈