playkit-sdk 1.1.0-beta-pr → 1.1.2-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 +281 -16
- package/dist/playkit-sdk.cjs.js.map +1 -1
- package/dist/playkit-sdk.d.ts +160 -2
- package/dist/playkit-sdk.esm.js +280 -17
- package/dist/playkit-sdk.esm.js.map +1 -1
- package/dist/playkit-sdk.umd.js +281 -16
- 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.2-beta
|
|
3
3
|
* PlayKit SDK for JavaScript
|
|
4
4
|
* @license SEE LICENSE IN LICENSE
|
|
5
5
|
*/
|
|
@@ -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
|
/**
|
|
@@ -1056,16 +1057,250 @@ class AuthFlowManager extends EventEmitter {
|
|
|
1056
1057
|
}
|
|
1057
1058
|
}
|
|
1058
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
|
+
* Shows an iframe modal 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
|
+
// Create iframe modal
|
|
1130
|
+
this.createAuthModal(authUrl);
|
|
1131
|
+
// Listen for messages from iframe
|
|
1132
|
+
const messageHandler = async (event) => {
|
|
1133
|
+
// Verify origin
|
|
1134
|
+
const allowedOrigin = new URL(this.baseURL).origin;
|
|
1135
|
+
if (event.origin !== allowedOrigin) {
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
// Check message type
|
|
1139
|
+
if (event.data.type !== 'external_auth_callback') {
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
// Cleanup
|
|
1143
|
+
window.removeEventListener('message', messageHandler);
|
|
1144
|
+
this.destroyAuthModal();
|
|
1145
|
+
try {
|
|
1146
|
+
// Extract data from message
|
|
1147
|
+
const { code, state: returnedState, error, error_description } = event.data;
|
|
1148
|
+
// Check for errors
|
|
1149
|
+
if (error) {
|
|
1150
|
+
reject(new PlayKitError(error_description || error, 'AUTH_ERROR'));
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
// Verify state
|
|
1154
|
+
if (returnedState !== state) {
|
|
1155
|
+
reject(new PlayKitError('State mismatch - possible CSRF attack', 'STATE_MISMATCH'));
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
if (!code) {
|
|
1159
|
+
reject(new PlayKitError('No authorization code received', 'NO_CODE'));
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
// Exchange code for token
|
|
1163
|
+
const token = await this.exchangeCodeForToken(code, codeVerifier, redirectUri);
|
|
1164
|
+
resolve(token);
|
|
1165
|
+
}
|
|
1166
|
+
catch (err) {
|
|
1167
|
+
reject(err);
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
window.addEventListener('message', messageHandler);
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Create authentication modal with iframe
|
|
1175
|
+
* @private
|
|
1176
|
+
*/
|
|
1177
|
+
createAuthModal(authUrl) {
|
|
1178
|
+
// Create modal container
|
|
1179
|
+
const modal = document.createElement('div');
|
|
1180
|
+
modal.id = 'playkit-external-auth-modal';
|
|
1181
|
+
modal.style.cssText = `
|
|
1182
|
+
position: fixed;
|
|
1183
|
+
top: 0;
|
|
1184
|
+
left: 0;
|
|
1185
|
+
right: 0;
|
|
1186
|
+
bottom: 0;
|
|
1187
|
+
z-index: 999999;
|
|
1188
|
+
display: flex;
|
|
1189
|
+
justify-content: center;
|
|
1190
|
+
align-items: center;
|
|
1191
|
+
background: rgba(0, 0, 0, 0.5);
|
|
1192
|
+
backdrop-filter: blur(4px);
|
|
1193
|
+
`;
|
|
1194
|
+
// Create close button
|
|
1195
|
+
const closeButton = document.createElement('button');
|
|
1196
|
+
closeButton.innerHTML = '×';
|
|
1197
|
+
closeButton.style.cssText = `
|
|
1198
|
+
position: absolute;
|
|
1199
|
+
top: 20px;
|
|
1200
|
+
right: 20px;
|
|
1201
|
+
width: 40px;
|
|
1202
|
+
height: 40px;
|
|
1203
|
+
border: none;
|
|
1204
|
+
background: rgba(255, 255, 255, 0.9);
|
|
1205
|
+
color: #333;
|
|
1206
|
+
font-size: 32px;
|
|
1207
|
+
line-height: 32px;
|
|
1208
|
+
cursor: pointer;
|
|
1209
|
+
border-radius: 50%;
|
|
1210
|
+
z-index: 1000000;
|
|
1211
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
1212
|
+
transition: background 0.2s;
|
|
1213
|
+
`;
|
|
1214
|
+
closeButton.onmouseover = () => {
|
|
1215
|
+
closeButton.style.background = 'rgba(255, 255, 255, 1)';
|
|
1216
|
+
};
|
|
1217
|
+
closeButton.onmouseout = () => {
|
|
1218
|
+
closeButton.style.background = 'rgba(255, 255, 255, 0.9)';
|
|
1219
|
+
};
|
|
1220
|
+
closeButton.onclick = () => {
|
|
1221
|
+
this.destroyAuthModal();
|
|
1222
|
+
};
|
|
1223
|
+
// Create iframe container
|
|
1224
|
+
const iframeContainer = document.createElement('div');
|
|
1225
|
+
iframeContainer.style.cssText = `
|
|
1226
|
+
position: relative;
|
|
1227
|
+
width: 90%;
|
|
1228
|
+
max-width: 480px;
|
|
1229
|
+
height: 90%;
|
|
1230
|
+
max-height: 700px;
|
|
1231
|
+
background: white;
|
|
1232
|
+
border-radius: 8px;
|
|
1233
|
+
overflow: hidden;
|
|
1234
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
|
1235
|
+
`;
|
|
1236
|
+
// Create iframe
|
|
1237
|
+
const iframe = document.createElement('iframe');
|
|
1238
|
+
iframe.src = authUrl;
|
|
1239
|
+
iframe.style.cssText = `
|
|
1240
|
+
width: 100%;
|
|
1241
|
+
height: 100%;
|
|
1242
|
+
border: none;
|
|
1243
|
+
`;
|
|
1244
|
+
iframe.allow = 'clipboard-write';
|
|
1245
|
+
iframeContainer.appendChild(iframe);
|
|
1246
|
+
modal.appendChild(closeButton);
|
|
1247
|
+
modal.appendChild(iframeContainer);
|
|
1248
|
+
document.body.appendChild(modal);
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* Destroy authentication modal
|
|
1252
|
+
* @private
|
|
1253
|
+
*/
|
|
1254
|
+
destroyAuthModal() {
|
|
1255
|
+
const modal = document.getElementById('playkit-external-auth-modal');
|
|
1256
|
+
if (modal) {
|
|
1257
|
+
modal.remove();
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Exchange authorization code for access token
|
|
1262
|
+
* @private
|
|
1263
|
+
*/
|
|
1264
|
+
async exchangeCodeForToken(code, codeVerifier, redirectUri) {
|
|
1265
|
+
const response = await fetch(`${this.baseURL}/api/external-auth/token`, {
|
|
1266
|
+
method: 'POST',
|
|
1267
|
+
headers: {
|
|
1268
|
+
'Content-Type': 'application/json',
|
|
1269
|
+
},
|
|
1270
|
+
body: JSON.stringify({
|
|
1271
|
+
grant_type: 'authorization_code',
|
|
1272
|
+
code: code,
|
|
1273
|
+
code_verifier: codeVerifier,
|
|
1274
|
+
redirect_uri: redirectUri,
|
|
1275
|
+
}),
|
|
1276
|
+
});
|
|
1277
|
+
if (!response.ok) {
|
|
1278
|
+
const error = await response.json().catch(() => ({ error_description: 'Token exchange failed' }));
|
|
1279
|
+
throw new PlayKitError(error.error_description || 'Token exchange failed', 'TOKEN_EXCHANGE_FAILED', response.status);
|
|
1280
|
+
}
|
|
1281
|
+
const data = await response.json();
|
|
1282
|
+
return data.access_token;
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Clean up
|
|
1286
|
+
*/
|
|
1287
|
+
destroy() {
|
|
1288
|
+
this.removeAllListeners();
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1059
1292
|
/**
|
|
1060
1293
|
* Authentication manager
|
|
1061
1294
|
* Handles JWT exchange and token management
|
|
1062
1295
|
*/
|
|
1063
|
-
|
|
1296
|
+
// @ts-ignore - replaced at build time
|
|
1297
|
+
const DEFAULT_BASE_URL$3 = "https://playkit.agentlandlab.com";
|
|
1064
1298
|
const JWT_EXCHANGE_ENDPOINT = '/api/external/exchange-jwt';
|
|
1065
1299
|
class AuthManager extends EventEmitter {
|
|
1066
1300
|
constructor(config) {
|
|
1067
1301
|
super();
|
|
1068
1302
|
this.authFlowManager = null;
|
|
1303
|
+
this.externalAuthFlowManager = null;
|
|
1069
1304
|
this.config = config;
|
|
1070
1305
|
this.storage = new TokenStorage();
|
|
1071
1306
|
this.baseURL = config.baseURL || DEFAULT_BASE_URL$3;
|
|
@@ -1119,7 +1354,9 @@ class AuthManager extends EventEmitter {
|
|
|
1119
1354
|
this.emit('unauthenticated');
|
|
1120
1355
|
// Auto-start login flow in browser environment
|
|
1121
1356
|
if (typeof window !== 'undefined') {
|
|
1122
|
-
|
|
1357
|
+
// Default to external-auth if not specified
|
|
1358
|
+
const useExternalAuth = this.config.authMethod !== 'headless';
|
|
1359
|
+
await this.startAuthFlow(useExternalAuth);
|
|
1123
1360
|
// If we reach here, authentication was successful
|
|
1124
1361
|
// If it failed, startAuthFlow() will have thrown an error
|
|
1125
1362
|
}
|
|
@@ -1130,27 +1367,53 @@ class AuthManager extends EventEmitter {
|
|
|
1130
1367
|
}
|
|
1131
1368
|
/**
|
|
1132
1369
|
* Start the authentication flow UI
|
|
1370
|
+
*
|
|
1371
|
+
* @param useExternalAuth - Use external-auth OAuth flow instead of headless flow
|
|
1133
1372
|
*/
|
|
1134
|
-
async startAuthFlow() {
|
|
1135
|
-
var _a;
|
|
1136
|
-
if (this.authFlowManager) {
|
|
1373
|
+
async startAuthFlow(useExternalAuth = false) {
|
|
1374
|
+
var _a, _b;
|
|
1375
|
+
if (this.authFlowManager || this.externalAuthFlowManager) {
|
|
1137
1376
|
// Already in progress
|
|
1138
1377
|
return;
|
|
1139
1378
|
}
|
|
1140
1379
|
try {
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1380
|
+
if (useExternalAuth) {
|
|
1381
|
+
// Use external-auth OAuth popup flow
|
|
1382
|
+
this.externalAuthFlowManager = new ExternalAuthFlowManager(this.baseURL, this.config.gameId);
|
|
1383
|
+
// Get player token directly from external-auth flow
|
|
1384
|
+
const playerToken = await this.externalAuthFlowManager.startFlow();
|
|
1385
|
+
// Update auth state with the player token
|
|
1386
|
+
this.authState = {
|
|
1387
|
+
isAuthenticated: true,
|
|
1388
|
+
token: playerToken,
|
|
1389
|
+
tokenType: 'player',
|
|
1390
|
+
};
|
|
1391
|
+
// Save to storage
|
|
1392
|
+
await this.storage.saveAuthState(this.config.gameId, this.authState);
|
|
1393
|
+
await this.storage.saveSharedToken(playerToken);
|
|
1394
|
+
this.emit('authenticated', this.authState);
|
|
1395
|
+
// Clean up
|
|
1396
|
+
this.externalAuthFlowManager.destroy();
|
|
1397
|
+
this.externalAuthFlowManager = null;
|
|
1398
|
+
}
|
|
1399
|
+
else {
|
|
1400
|
+
// Use headless verification code flow
|
|
1401
|
+
this.authFlowManager = new AuthFlowManager(this.baseURL);
|
|
1402
|
+
// Get global token from auth flow
|
|
1403
|
+
const globalToken = await this.authFlowManager.startFlow();
|
|
1404
|
+
// Exchange for player token
|
|
1405
|
+
await this.exchangeJWT(globalToken);
|
|
1406
|
+
// Clean up
|
|
1407
|
+
this.authFlowManager.destroy();
|
|
1408
|
+
this.authFlowManager = null;
|
|
1409
|
+
}
|
|
1149
1410
|
}
|
|
1150
1411
|
catch (error) {
|
|
1151
1412
|
// User canceled or error occurred
|
|
1152
1413
|
(_a = this.authFlowManager) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
1153
1414
|
this.authFlowManager = null;
|
|
1415
|
+
(_b = this.externalAuthFlowManager) === null || _b === void 0 ? void 0 : _b.destroy();
|
|
1416
|
+
this.externalAuthFlowManager = null;
|
|
1154
1417
|
// Re-emit error
|
|
1155
1418
|
this.emit('error', error);
|
|
1156
1419
|
throw error;
|
|
@@ -2791,8 +3054,10 @@ class PlayKitSDK extends EventEmitter {
|
|
|
2791
3054
|
}
|
|
2792
3055
|
}
|
|
2793
3056
|
|
|
3057
|
+
exports.AuthFlowManager = AuthFlowManager;
|
|
2794
3058
|
exports.AuthManager = AuthManager;
|
|
2795
3059
|
exports.ChatClient = ChatClient;
|
|
3060
|
+
exports.ExternalAuthFlowManager = ExternalAuthFlowManager;
|
|
2796
3061
|
exports.ImageClient = ImageClient;
|
|
2797
3062
|
exports.NPCClient = NPCClient;
|
|
2798
3063
|
exports.PlayKitSDK = PlayKitSDK;
|