PLAYUS 안에서 게임 실행
PLAYUS의 실행 화면은 등록된 launchUrl을 iframe으로 엽니다. 로그인 코드는 주소가 아닌 postMessage로 전달합니다.
핸드셰이크#
- PLAYUS가 launchUrl에 playus=1, playus_host, lang 쿼리를 붙여 엽니다.
- 게임이 playus:ready를 부모 창에 보냅니다.
- PLAYUS가 iframe의 origin과 source를 확인하고 playus:launch와 일회용 code를 보냅니다.
- 게임 브라우저가 자기 서버로 code를 보내고, 게임 서버가 PLAYUS 토큰 API에서 교환합니다.
- 게임 세션을 발급한 뒤 playus:signed-in을 보냅니다.
브라우저 구현#
import { playusHost, waitForLaunchCode, notifyPlayus } from './playus-embed.js';
const host = playusHost();
if (host) {
const code = await waitForLaunchCode(host);
const response = await fetch('/api/auth/playus/launch', {
method: 'POST', credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code }),
});
if (!response.ok) throw new Error('게임 로그인 실패');
notifyPlayus(host, 'signed-in');
}
/api/auth/playus/launch는 게임에서 구현하는 엔드포인트입니다. CSRF와 Origin 검사, 요청 상한을 적용합니다. iframe의 쿠키 제한을 고려해 게임 세션 유지 방식도 실제 브라우저에서 확인하세요.
게임 서버 구현#
const identity = await playus.exchangeCode(code, {
redirectUri: process.env.PLAYUS_LAUNCH_URL!,
});
// launchUrl은 쿼리를 추가하기 전 등록 주소 그대로 사용합니다.
// identity.sub로 게임 계정을 연결한 후 게임 자체 세션을 발급합니다.
실행 코드는 120초·일회용입니다. 만료나 재시도 시 새 ready 핸드셰이크를 시작하세요. 코드를 로그, URL, localStorage 또는 분석 도구에 저장하지 마세요.
메시지 규약#
| 방향 | type | 추가 데이터 / 의미 |
|---|---|---|
| 게임 → PLAYUS | playus:ready | v: 1, 코드 수신 준비 |
| PLAYUS → 게임 | playus:launch | code, v: 1 |
| 게임 → PLAYUS | playus:signed-in | 로그인 완료 |
| 게임 → PLAYUS | playus:reward | 지갑 표시 다시 조회 |
| 게임 → PLAYUS | playus:close | 실행 화면 닫기 |
reward 메시지는 지급 요청이 아닙니다. 서버 API에서 실제 지급을 확인한 뒤 화면 갱신 알림으로만 사용하세요.
iframe 보안 설정#
게임의 /playus 응답에 Content-Security-Policy: frame-ancestors https://www.playus.app https://www.playus.io 를 설정합니다. 개발 도메인은 개발 환경에서만 추가하세요. 이 경로에 X-Frame-Options: DENY 또는 SAMEORIGIN이 있으면 임베드가 차단됩니다. PLAYUS 쪽에서도 게임 origin이 frame-src에 허용되어야 합니다.
브라우저 키트는 허용된 playus_host, 부모 창 e.source, e.origin을 검사합니다. 직접 구현할 때도 같은 검사를 유지하고 postMessage의 targetOrigin에 *를 쓰지 마세요.
embed=false 앱은 게임 사이트로 이동한 뒤 표준 OAuth 로그인을 시작하는 방식으로 연동합니다.