playkit-sdk 1.1.0-beta → 1.1.1-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/playkit-sdk.cjs.js +218 -21
- package/dist/playkit-sdk.cjs.js.map +1 -1
- package/dist/playkit-sdk.d.ts +150 -2
- package/dist/playkit-sdk.esm.js +217 -22
- package/dist/playkit-sdk.esm.js.map +1 -1
- package/dist/playkit-sdk.umd.js +218 -21
- package/dist/playkit-sdk.umd.js.map +1 -1
- package/package.json +4 -1
package/dist/playkit-sdk.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* playkit-sdk v1.1.
|
|
2
|
+
* playkit-sdk v1.1.1-beta
|
|
3
3
|
* PlayKit SDK for JavaScript
|
|
4
4
|
* @license SEE LICENSE IN LICENSE
|
|
5
5
|
*/
|
|
@@ -210,7 +210,7 @@ class TokenStorage {
|
|
|
210
210
|
const translations$1 = {
|
|
211
211
|
en: {
|
|
212
212
|
signIn: 'Sign In / Register',
|
|
213
|
-
signInSubtitle: '
|
|
213
|
+
signInSubtitle: 'This game uses PlayKit for cost management.\nIf you don\'t have an account, we\'ll automatically register you. Sign up and get free AI game credits!',
|
|
214
214
|
email: 'Email',
|
|
215
215
|
phone: 'Phone',
|
|
216
216
|
emailPlaceholder: 'Enter your email address',
|
|
@@ -228,7 +228,7 @@ const translations$1 = {
|
|
|
228
228
|
},
|
|
229
229
|
zh: {
|
|
230
230
|
signIn: '登录/注册',
|
|
231
|
-
signInSubtitle: '
|
|
231
|
+
signInSubtitle: '本游戏使用PlayKit进行成本管理。\n如果您没有帐户,我们会为您自动注册。注册即送AI游戏积分!',
|
|
232
232
|
email: '邮箱',
|
|
233
233
|
phone: '手机',
|
|
234
234
|
emailPlaceholder: '请输入邮箱地址',
|
|
@@ -246,7 +246,7 @@ const translations$1 = {
|
|
|
246
246
|
},
|
|
247
247
|
'zh-TW': {
|
|
248
248
|
signIn: '登入/註冊',
|
|
249
|
-
signInSubtitle: '
|
|
249
|
+
signInSubtitle: '本遊戲使用PlayKit進行成本管理。\n如果您沒有帳戶,我們會為您自動註冊。註冊即送AI遊戲積分!',
|
|
250
250
|
email: '電子郵件',
|
|
251
251
|
phone: '手機',
|
|
252
252
|
emailPlaceholder: '請輸入電子郵件地址',
|
|
@@ -264,7 +264,7 @@ const translations$1 = {
|
|
|
264
264
|
},
|
|
265
265
|
ja: {
|
|
266
266
|
signIn: 'サインイン/登録',
|
|
267
|
-
signInSubtitle: '
|
|
267
|
+
signInSubtitle: 'このゲームはPlayKitでコスト管理を行っています。\nアカウントをお持ちでない場合は、自動的に登録します。登録するとAIゲームクレジットがもらえます!',
|
|
268
268
|
email: 'メール',
|
|
269
269
|
phone: '電話',
|
|
270
270
|
emailPlaceholder: 'メールアドレスを入力してください',
|
|
@@ -282,7 +282,7 @@ const translations$1 = {
|
|
|
282
282
|
},
|
|
283
283
|
ko: {
|
|
284
284
|
signIn: '로그인/가입',
|
|
285
|
-
signInSubtitle: '계정이 없으시면 자동으로
|
|
285
|
+
signInSubtitle: '이 게임은 PlayKit으로 비용 관리를 합니다.\n계정이 없으시면 자동으로 등록해 드립니다. 가입하면 AI 게임 크레딧을 받으세요!',
|
|
286
286
|
email: '이메일',
|
|
287
287
|
phone: '전화',
|
|
288
288
|
emailPlaceholder: '이메일 주소를 입력하세요',
|
|
@@ -300,7 +300,7 @@ const translations$1 = {
|
|
|
300
300
|
},
|
|
301
301
|
};
|
|
302
302
|
class AuthFlowManager extends EventEmitter {
|
|
303
|
-
constructor(baseURL
|
|
303
|
+
constructor(baseURL) {
|
|
304
304
|
super();
|
|
305
305
|
this.currentSessionId = null;
|
|
306
306
|
this.uiContainer = null;
|
|
@@ -311,7 +311,8 @@ class AuthFlowManager extends EventEmitter {
|
|
|
311
311
|
this.identifierPanel = null;
|
|
312
312
|
this.verificationPanel = null;
|
|
313
313
|
this.loadingOverlay = null;
|
|
314
|
-
|
|
314
|
+
// @ts-ignore - replaced at build time
|
|
315
|
+
this.baseURL = baseURL || "https://playkit.agentlandlab.com";
|
|
315
316
|
this.currentLanguage = this.detectLanguage();
|
|
316
317
|
}
|
|
317
318
|
/**
|
|
@@ -801,6 +802,10 @@ class AuthFlowManager extends EventEmitter {
|
|
|
801
802
|
codeInputs === null || codeInputs === void 0 ? void 0 : codeInputs.forEach((input, index) => {
|
|
802
803
|
input.addEventListener('input', (e) => {
|
|
803
804
|
const target = e.target;
|
|
805
|
+
// Only allow digits and limit to 1 character
|
|
806
|
+
const value = target.value.replace(/\D/g, '');
|
|
807
|
+
target.value = value.slice(0, 1);
|
|
808
|
+
// Move to next input if a digit was entered
|
|
804
809
|
if (target.value.length === 1 && index < codeInputs.length - 1) {
|
|
805
810
|
codeInputs[index + 1].focus();
|
|
806
811
|
}
|
|
@@ -1052,16 +1057,178 @@ class AuthFlowManager extends EventEmitter {
|
|
|
1052
1057
|
}
|
|
1053
1058
|
}
|
|
1054
1059
|
|
|
1060
|
+
/**
|
|
1061
|
+
* External Authentication Flow Manager
|
|
1062
|
+
* Manages OAuth-like popup authentication flow for external_auth channel
|
|
1063
|
+
*/
|
|
1064
|
+
class ExternalAuthFlowManager extends EventEmitter {
|
|
1065
|
+
constructor(baseURL, gameId) {
|
|
1066
|
+
super();
|
|
1067
|
+
this.baseURL = baseURL;
|
|
1068
|
+
this.gameId = gameId;
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Generate a random string for PKCE code verifier
|
|
1072
|
+
* @private
|
|
1073
|
+
*/
|
|
1074
|
+
generateCodeVerifier() {
|
|
1075
|
+
const array = new Uint8Array(32);
|
|
1076
|
+
crypto.getRandomValues(array);
|
|
1077
|
+
return this.base64URLEncode(array);
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Generate PKCE code challenge from verifier
|
|
1081
|
+
* @private
|
|
1082
|
+
*/
|
|
1083
|
+
async generateCodeChallenge(verifier) {
|
|
1084
|
+
const encoder = new TextEncoder();
|
|
1085
|
+
const data = encoder.encode(verifier);
|
|
1086
|
+
const hash = await crypto.subtle.digest('SHA-256', data);
|
|
1087
|
+
return this.base64URLEncode(new Uint8Array(hash));
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Base64 URL encode
|
|
1091
|
+
* @private
|
|
1092
|
+
*/
|
|
1093
|
+
base64URLEncode(buffer) {
|
|
1094
|
+
const base64 = btoa(String.fromCharCode(...buffer));
|
|
1095
|
+
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Generate a random state for CSRF protection
|
|
1099
|
+
* @private
|
|
1100
|
+
*/
|
|
1101
|
+
generateState() {
|
|
1102
|
+
const array = new Uint8Array(16);
|
|
1103
|
+
crypto.getRandomValues(array);
|
|
1104
|
+
return this.base64URLEncode(array);
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* Start the external authentication flow
|
|
1108
|
+
* Opens a popup window for user to login and authorize
|
|
1109
|
+
*
|
|
1110
|
+
* @returns {Promise<string>} Player token
|
|
1111
|
+
*/
|
|
1112
|
+
async startFlow() {
|
|
1113
|
+
return new Promise(async (resolve, reject) => {
|
|
1114
|
+
// Generate PKCE parameters
|
|
1115
|
+
const codeVerifier = this.generateCodeVerifier();
|
|
1116
|
+
const codeChallenge = await this.generateCodeChallenge(codeVerifier);
|
|
1117
|
+
const state = this.generateState();
|
|
1118
|
+
// Build authorization URL with postmessage redirect
|
|
1119
|
+
const redirectUri = 'postmessage';
|
|
1120
|
+
const params = new URLSearchParams({
|
|
1121
|
+
response_type: 'code',
|
|
1122
|
+
game_id: this.gameId,
|
|
1123
|
+
redirect_uri: redirectUri,
|
|
1124
|
+
code_challenge: codeChallenge,
|
|
1125
|
+
code_challenge_method: 'S256',
|
|
1126
|
+
state: state,
|
|
1127
|
+
});
|
|
1128
|
+
const authUrl = `${this.baseURL}/external-auth/authorize?${params}`;
|
|
1129
|
+
// Open popup window
|
|
1130
|
+
const popup = window.open(authUrl, 'agentland_external_auth', 'width=500,height=700,scrollbars=yes');
|
|
1131
|
+
if (!popup) {
|
|
1132
|
+
reject(new PlayKitError('Failed to open popup window. Please allow popups for this site.', 'POPUP_BLOCKED'));
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
// Listen for messages from popup
|
|
1136
|
+
const messageHandler = async (event) => {
|
|
1137
|
+
// Verify origin
|
|
1138
|
+
const allowedOrigin = new URL(this.baseURL).origin;
|
|
1139
|
+
if (event.origin !== allowedOrigin) {
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
// Check message type
|
|
1143
|
+
if (event.data.type !== 'external_auth_callback') {
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
// Cleanup
|
|
1147
|
+
window.removeEventListener('message', messageHandler);
|
|
1148
|
+
clearInterval(checkClosed);
|
|
1149
|
+
if (popup && !popup.closed) {
|
|
1150
|
+
popup.close();
|
|
1151
|
+
}
|
|
1152
|
+
try {
|
|
1153
|
+
// Extract data from message
|
|
1154
|
+
const { code, state: returnedState, error, error_description } = event.data;
|
|
1155
|
+
// Check for errors
|
|
1156
|
+
if (error) {
|
|
1157
|
+
reject(new PlayKitError(error_description || error, 'AUTH_ERROR'));
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
// Verify state
|
|
1161
|
+
if (returnedState !== state) {
|
|
1162
|
+
reject(new PlayKitError('State mismatch - possible CSRF attack', 'STATE_MISMATCH'));
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
if (!code) {
|
|
1166
|
+
reject(new PlayKitError('No authorization code received', 'NO_CODE'));
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
// Exchange code for token
|
|
1170
|
+
const token = await this.exchangeCodeForToken(code, codeVerifier, redirectUri);
|
|
1171
|
+
resolve(token);
|
|
1172
|
+
}
|
|
1173
|
+
catch (err) {
|
|
1174
|
+
reject(err);
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
window.addEventListener('message', messageHandler);
|
|
1178
|
+
// Check if popup was closed without completing auth
|
|
1179
|
+
const checkClosed = setInterval(() => {
|
|
1180
|
+
if (popup.closed) {
|
|
1181
|
+
clearInterval(checkClosed);
|
|
1182
|
+
window.removeEventListener('message', messageHandler);
|
|
1183
|
+
reject(new PlayKitError('Authentication cancelled by user', 'USER_CANCELLED'));
|
|
1184
|
+
}
|
|
1185
|
+
}, 1000);
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Exchange authorization code for access token
|
|
1190
|
+
* @private
|
|
1191
|
+
*/
|
|
1192
|
+
async exchangeCodeForToken(code, codeVerifier, redirectUri) {
|
|
1193
|
+
const response = await fetch(`${this.baseURL}/api/external-auth/token`, {
|
|
1194
|
+
method: 'POST',
|
|
1195
|
+
headers: {
|
|
1196
|
+
'Content-Type': 'application/json',
|
|
1197
|
+
},
|
|
1198
|
+
body: JSON.stringify({
|
|
1199
|
+
grant_type: 'authorization_code',
|
|
1200
|
+
code: code,
|
|
1201
|
+
code_verifier: codeVerifier,
|
|
1202
|
+
redirect_uri: redirectUri,
|
|
1203
|
+
}),
|
|
1204
|
+
});
|
|
1205
|
+
if (!response.ok) {
|
|
1206
|
+
const error = await response.json().catch(() => ({ error_description: 'Token exchange failed' }));
|
|
1207
|
+
throw new PlayKitError(error.error_description || 'Token exchange failed', 'TOKEN_EXCHANGE_FAILED', response.status);
|
|
1208
|
+
}
|
|
1209
|
+
const data = await response.json();
|
|
1210
|
+
return data.access_token;
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Clean up
|
|
1214
|
+
*/
|
|
1215
|
+
destroy() {
|
|
1216
|
+
this.removeAllListeners();
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1055
1220
|
/**
|
|
1056
1221
|
* Authentication manager
|
|
1057
1222
|
* Handles JWT exchange and token management
|
|
1058
1223
|
*/
|
|
1059
|
-
|
|
1224
|
+
// @ts-ignore - replaced at build time
|
|
1225
|
+
const DEFAULT_BASE_URL$3 = "https://playkit.agentlandlab.com";
|
|
1060
1226
|
const JWT_EXCHANGE_ENDPOINT = '/api/external/exchange-jwt';
|
|
1061
1227
|
class AuthManager extends EventEmitter {
|
|
1062
1228
|
constructor(config) {
|
|
1063
1229
|
super();
|
|
1064
1230
|
this.authFlowManager = null;
|
|
1231
|
+
this.externalAuthFlowManager = null;
|
|
1065
1232
|
this.config = config;
|
|
1066
1233
|
this.storage = new TokenStorage();
|
|
1067
1234
|
this.baseURL = config.baseURL || DEFAULT_BASE_URL$3;
|
|
@@ -1115,7 +1282,9 @@ class AuthManager extends EventEmitter {
|
|
|
1115
1282
|
this.emit('unauthenticated');
|
|
1116
1283
|
// Auto-start login flow in browser environment
|
|
1117
1284
|
if (typeof window !== 'undefined') {
|
|
1118
|
-
|
|
1285
|
+
// Default to external-auth if not specified
|
|
1286
|
+
const useExternalAuth = this.config.authMethod !== 'headless';
|
|
1287
|
+
await this.startAuthFlow(useExternalAuth);
|
|
1119
1288
|
// If we reach here, authentication was successful
|
|
1120
1289
|
// If it failed, startAuthFlow() will have thrown an error
|
|
1121
1290
|
}
|
|
@@ -1126,27 +1295,53 @@ class AuthManager extends EventEmitter {
|
|
|
1126
1295
|
}
|
|
1127
1296
|
/**
|
|
1128
1297
|
* Start the authentication flow UI
|
|
1298
|
+
*
|
|
1299
|
+
* @param useExternalAuth - Use external-auth OAuth flow instead of headless flow
|
|
1129
1300
|
*/
|
|
1130
|
-
async startAuthFlow() {
|
|
1131
|
-
var _a;
|
|
1132
|
-
if (this.authFlowManager) {
|
|
1301
|
+
async startAuthFlow(useExternalAuth = false) {
|
|
1302
|
+
var _a, _b;
|
|
1303
|
+
if (this.authFlowManager || this.externalAuthFlowManager) {
|
|
1133
1304
|
// Already in progress
|
|
1134
1305
|
return;
|
|
1135
1306
|
}
|
|
1136
1307
|
try {
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1308
|
+
if (useExternalAuth) {
|
|
1309
|
+
// Use external-auth OAuth popup flow
|
|
1310
|
+
this.externalAuthFlowManager = new ExternalAuthFlowManager(this.baseURL, this.config.gameId);
|
|
1311
|
+
// Get player token directly from external-auth flow
|
|
1312
|
+
const playerToken = await this.externalAuthFlowManager.startFlow();
|
|
1313
|
+
// Update auth state with the player token
|
|
1314
|
+
this.authState = {
|
|
1315
|
+
isAuthenticated: true,
|
|
1316
|
+
token: playerToken,
|
|
1317
|
+
tokenType: 'player',
|
|
1318
|
+
};
|
|
1319
|
+
// Save to storage
|
|
1320
|
+
await this.storage.saveAuthState(this.config.gameId, this.authState);
|
|
1321
|
+
await this.storage.saveSharedToken(playerToken);
|
|
1322
|
+
this.emit('authenticated', this.authState);
|
|
1323
|
+
// Clean up
|
|
1324
|
+
this.externalAuthFlowManager.destroy();
|
|
1325
|
+
this.externalAuthFlowManager = null;
|
|
1326
|
+
}
|
|
1327
|
+
else {
|
|
1328
|
+
// Use headless verification code flow
|
|
1329
|
+
this.authFlowManager = new AuthFlowManager(this.baseURL);
|
|
1330
|
+
// Get global token from auth flow
|
|
1331
|
+
const globalToken = await this.authFlowManager.startFlow();
|
|
1332
|
+
// Exchange for player token
|
|
1333
|
+
await this.exchangeJWT(globalToken);
|
|
1334
|
+
// Clean up
|
|
1335
|
+
this.authFlowManager.destroy();
|
|
1336
|
+
this.authFlowManager = null;
|
|
1337
|
+
}
|
|
1145
1338
|
}
|
|
1146
1339
|
catch (error) {
|
|
1147
1340
|
// User canceled or error occurred
|
|
1148
1341
|
(_a = this.authFlowManager) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
1149
1342
|
this.authFlowManager = null;
|
|
1343
|
+
(_b = this.externalAuthFlowManager) === null || _b === void 0 ? void 0 : _b.destroy();
|
|
1344
|
+
this.externalAuthFlowManager = null;
|
|
1150
1345
|
// Re-emit error
|
|
1151
1346
|
this.emit('error', error);
|
|
1152
1347
|
throw error;
|
|
@@ -2787,8 +2982,10 @@ class PlayKitSDK extends EventEmitter {
|
|
|
2787
2982
|
}
|
|
2788
2983
|
}
|
|
2789
2984
|
|
|
2985
|
+
exports.AuthFlowManager = AuthFlowManager;
|
|
2790
2986
|
exports.AuthManager = AuthManager;
|
|
2791
2987
|
exports.ChatClient = ChatClient;
|
|
2988
|
+
exports.ExternalAuthFlowManager = ExternalAuthFlowManager;
|
|
2792
2989
|
exports.ImageClient = ImageClient;
|
|
2793
2990
|
exports.NPCClient = NPCClient;
|
|
2794
2991
|
exports.PlayKitSDK = PlayKitSDK;
|