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.esm.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
|
*/
|
|
@@ -296,7 +296,7 @@ const translations$1 = {
|
|
|
296
296
|
},
|
|
297
297
|
};
|
|
298
298
|
class AuthFlowManager extends EventEmitter {
|
|
299
|
-
constructor(baseURL
|
|
299
|
+
constructor(baseURL) {
|
|
300
300
|
super();
|
|
301
301
|
this.currentSessionId = null;
|
|
302
302
|
this.uiContainer = null;
|
|
@@ -307,7 +307,8 @@ class AuthFlowManager extends EventEmitter {
|
|
|
307
307
|
this.identifierPanel = null;
|
|
308
308
|
this.verificationPanel = null;
|
|
309
309
|
this.loadingOverlay = null;
|
|
310
|
-
|
|
310
|
+
// @ts-ignore - replaced at build time
|
|
311
|
+
this.baseURL = baseURL || "https://playkit.agentlandlab.com";
|
|
311
312
|
this.currentLanguage = this.detectLanguage();
|
|
312
313
|
}
|
|
313
314
|
/**
|
|
@@ -1052,16 +1053,250 @@ class AuthFlowManager extends EventEmitter {
|
|
|
1052
1053
|
}
|
|
1053
1054
|
}
|
|
1054
1055
|
|
|
1056
|
+
/**
|
|
1057
|
+
* External Authentication Flow Manager
|
|
1058
|
+
* Manages OAuth-like popup authentication flow for external_auth channel
|
|
1059
|
+
*/
|
|
1060
|
+
class ExternalAuthFlowManager extends EventEmitter {
|
|
1061
|
+
constructor(baseURL, gameId) {
|
|
1062
|
+
super();
|
|
1063
|
+
this.baseURL = baseURL;
|
|
1064
|
+
this.gameId = gameId;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Generate a random string for PKCE code verifier
|
|
1068
|
+
* @private
|
|
1069
|
+
*/
|
|
1070
|
+
generateCodeVerifier() {
|
|
1071
|
+
const array = new Uint8Array(32);
|
|
1072
|
+
crypto.getRandomValues(array);
|
|
1073
|
+
return this.base64URLEncode(array);
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Generate PKCE code challenge from verifier
|
|
1077
|
+
* @private
|
|
1078
|
+
*/
|
|
1079
|
+
async generateCodeChallenge(verifier) {
|
|
1080
|
+
const encoder = new TextEncoder();
|
|
1081
|
+
const data = encoder.encode(verifier);
|
|
1082
|
+
const hash = await crypto.subtle.digest('SHA-256', data);
|
|
1083
|
+
return this.base64URLEncode(new Uint8Array(hash));
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Base64 URL encode
|
|
1087
|
+
* @private
|
|
1088
|
+
*/
|
|
1089
|
+
base64URLEncode(buffer) {
|
|
1090
|
+
const base64 = btoa(String.fromCharCode(...buffer));
|
|
1091
|
+
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Generate a random state for CSRF protection
|
|
1095
|
+
* @private
|
|
1096
|
+
*/
|
|
1097
|
+
generateState() {
|
|
1098
|
+
const array = new Uint8Array(16);
|
|
1099
|
+
crypto.getRandomValues(array);
|
|
1100
|
+
return this.base64URLEncode(array);
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Start the external authentication flow
|
|
1104
|
+
* Shows an iframe modal for user to login and authorize
|
|
1105
|
+
*
|
|
1106
|
+
* @returns {Promise<string>} Player token
|
|
1107
|
+
*/
|
|
1108
|
+
async startFlow() {
|
|
1109
|
+
return new Promise(async (resolve, reject) => {
|
|
1110
|
+
// Generate PKCE parameters
|
|
1111
|
+
const codeVerifier = this.generateCodeVerifier();
|
|
1112
|
+
const codeChallenge = await this.generateCodeChallenge(codeVerifier);
|
|
1113
|
+
const state = this.generateState();
|
|
1114
|
+
// Build authorization URL with postmessage redirect
|
|
1115
|
+
const redirectUri = 'postmessage';
|
|
1116
|
+
const params = new URLSearchParams({
|
|
1117
|
+
response_type: 'code',
|
|
1118
|
+
game_id: this.gameId,
|
|
1119
|
+
redirect_uri: redirectUri,
|
|
1120
|
+
code_challenge: codeChallenge,
|
|
1121
|
+
code_challenge_method: 'S256',
|
|
1122
|
+
state: state,
|
|
1123
|
+
});
|
|
1124
|
+
const authUrl = `${this.baseURL}/external-auth/authorize?${params}`;
|
|
1125
|
+
// Create iframe modal
|
|
1126
|
+
this.createAuthModal(authUrl);
|
|
1127
|
+
// Listen for messages from iframe
|
|
1128
|
+
const messageHandler = async (event) => {
|
|
1129
|
+
// Verify origin
|
|
1130
|
+
const allowedOrigin = new URL(this.baseURL).origin;
|
|
1131
|
+
if (event.origin !== allowedOrigin) {
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
// Check message type
|
|
1135
|
+
if (event.data.type !== 'external_auth_callback') {
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
// Cleanup
|
|
1139
|
+
window.removeEventListener('message', messageHandler);
|
|
1140
|
+
this.destroyAuthModal();
|
|
1141
|
+
try {
|
|
1142
|
+
// Extract data from message
|
|
1143
|
+
const { code, state: returnedState, error, error_description } = event.data;
|
|
1144
|
+
// Check for errors
|
|
1145
|
+
if (error) {
|
|
1146
|
+
reject(new PlayKitError(error_description || error, 'AUTH_ERROR'));
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
// Verify state
|
|
1150
|
+
if (returnedState !== state) {
|
|
1151
|
+
reject(new PlayKitError('State mismatch - possible CSRF attack', 'STATE_MISMATCH'));
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
if (!code) {
|
|
1155
|
+
reject(new PlayKitError('No authorization code received', 'NO_CODE'));
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
// Exchange code for token
|
|
1159
|
+
const token = await this.exchangeCodeForToken(code, codeVerifier, redirectUri);
|
|
1160
|
+
resolve(token);
|
|
1161
|
+
}
|
|
1162
|
+
catch (err) {
|
|
1163
|
+
reject(err);
|
|
1164
|
+
}
|
|
1165
|
+
};
|
|
1166
|
+
window.addEventListener('message', messageHandler);
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Create authentication modal with iframe
|
|
1171
|
+
* @private
|
|
1172
|
+
*/
|
|
1173
|
+
createAuthModal(authUrl) {
|
|
1174
|
+
// Create modal container
|
|
1175
|
+
const modal = document.createElement('div');
|
|
1176
|
+
modal.id = 'playkit-external-auth-modal';
|
|
1177
|
+
modal.style.cssText = `
|
|
1178
|
+
position: fixed;
|
|
1179
|
+
top: 0;
|
|
1180
|
+
left: 0;
|
|
1181
|
+
right: 0;
|
|
1182
|
+
bottom: 0;
|
|
1183
|
+
z-index: 999999;
|
|
1184
|
+
display: flex;
|
|
1185
|
+
justify-content: center;
|
|
1186
|
+
align-items: center;
|
|
1187
|
+
background: rgba(0, 0, 0, 0.5);
|
|
1188
|
+
backdrop-filter: blur(4px);
|
|
1189
|
+
`;
|
|
1190
|
+
// Create close button
|
|
1191
|
+
const closeButton = document.createElement('button');
|
|
1192
|
+
closeButton.innerHTML = '×';
|
|
1193
|
+
closeButton.style.cssText = `
|
|
1194
|
+
position: absolute;
|
|
1195
|
+
top: 20px;
|
|
1196
|
+
right: 20px;
|
|
1197
|
+
width: 40px;
|
|
1198
|
+
height: 40px;
|
|
1199
|
+
border: none;
|
|
1200
|
+
background: rgba(255, 255, 255, 0.9);
|
|
1201
|
+
color: #333;
|
|
1202
|
+
font-size: 32px;
|
|
1203
|
+
line-height: 32px;
|
|
1204
|
+
cursor: pointer;
|
|
1205
|
+
border-radius: 50%;
|
|
1206
|
+
z-index: 1000000;
|
|
1207
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
1208
|
+
transition: background 0.2s;
|
|
1209
|
+
`;
|
|
1210
|
+
closeButton.onmouseover = () => {
|
|
1211
|
+
closeButton.style.background = 'rgba(255, 255, 255, 1)';
|
|
1212
|
+
};
|
|
1213
|
+
closeButton.onmouseout = () => {
|
|
1214
|
+
closeButton.style.background = 'rgba(255, 255, 255, 0.9)';
|
|
1215
|
+
};
|
|
1216
|
+
closeButton.onclick = () => {
|
|
1217
|
+
this.destroyAuthModal();
|
|
1218
|
+
};
|
|
1219
|
+
// Create iframe container
|
|
1220
|
+
const iframeContainer = document.createElement('div');
|
|
1221
|
+
iframeContainer.style.cssText = `
|
|
1222
|
+
position: relative;
|
|
1223
|
+
width: 90%;
|
|
1224
|
+
max-width: 480px;
|
|
1225
|
+
height: 90%;
|
|
1226
|
+
max-height: 700px;
|
|
1227
|
+
background: white;
|
|
1228
|
+
border-radius: 8px;
|
|
1229
|
+
overflow: hidden;
|
|
1230
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
|
1231
|
+
`;
|
|
1232
|
+
// Create iframe
|
|
1233
|
+
const iframe = document.createElement('iframe');
|
|
1234
|
+
iframe.src = authUrl;
|
|
1235
|
+
iframe.style.cssText = `
|
|
1236
|
+
width: 100%;
|
|
1237
|
+
height: 100%;
|
|
1238
|
+
border: none;
|
|
1239
|
+
`;
|
|
1240
|
+
iframe.allow = 'clipboard-write';
|
|
1241
|
+
iframeContainer.appendChild(iframe);
|
|
1242
|
+
modal.appendChild(closeButton);
|
|
1243
|
+
modal.appendChild(iframeContainer);
|
|
1244
|
+
document.body.appendChild(modal);
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Destroy authentication modal
|
|
1248
|
+
* @private
|
|
1249
|
+
*/
|
|
1250
|
+
destroyAuthModal() {
|
|
1251
|
+
const modal = document.getElementById('playkit-external-auth-modal');
|
|
1252
|
+
if (modal) {
|
|
1253
|
+
modal.remove();
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Exchange authorization code for access token
|
|
1258
|
+
* @private
|
|
1259
|
+
*/
|
|
1260
|
+
async exchangeCodeForToken(code, codeVerifier, redirectUri) {
|
|
1261
|
+
const response = await fetch(`${this.baseURL}/api/external-auth/token`, {
|
|
1262
|
+
method: 'POST',
|
|
1263
|
+
headers: {
|
|
1264
|
+
'Content-Type': 'application/json',
|
|
1265
|
+
},
|
|
1266
|
+
body: JSON.stringify({
|
|
1267
|
+
grant_type: 'authorization_code',
|
|
1268
|
+
code: code,
|
|
1269
|
+
code_verifier: codeVerifier,
|
|
1270
|
+
redirect_uri: redirectUri,
|
|
1271
|
+
}),
|
|
1272
|
+
});
|
|
1273
|
+
if (!response.ok) {
|
|
1274
|
+
const error = await response.json().catch(() => ({ error_description: 'Token exchange failed' }));
|
|
1275
|
+
throw new PlayKitError(error.error_description || 'Token exchange failed', 'TOKEN_EXCHANGE_FAILED', response.status);
|
|
1276
|
+
}
|
|
1277
|
+
const data = await response.json();
|
|
1278
|
+
return data.access_token;
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* Clean up
|
|
1282
|
+
*/
|
|
1283
|
+
destroy() {
|
|
1284
|
+
this.removeAllListeners();
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1055
1288
|
/**
|
|
1056
1289
|
* Authentication manager
|
|
1057
1290
|
* Handles JWT exchange and token management
|
|
1058
1291
|
*/
|
|
1059
|
-
|
|
1292
|
+
// @ts-ignore - replaced at build time
|
|
1293
|
+
const DEFAULT_BASE_URL$3 = "https://playkit.agentlandlab.com";
|
|
1060
1294
|
const JWT_EXCHANGE_ENDPOINT = '/api/external/exchange-jwt';
|
|
1061
1295
|
class AuthManager extends EventEmitter {
|
|
1062
1296
|
constructor(config) {
|
|
1063
1297
|
super();
|
|
1064
1298
|
this.authFlowManager = null;
|
|
1299
|
+
this.externalAuthFlowManager = null;
|
|
1065
1300
|
this.config = config;
|
|
1066
1301
|
this.storage = new TokenStorage();
|
|
1067
1302
|
this.baseURL = config.baseURL || DEFAULT_BASE_URL$3;
|
|
@@ -1115,7 +1350,9 @@ class AuthManager extends EventEmitter {
|
|
|
1115
1350
|
this.emit('unauthenticated');
|
|
1116
1351
|
// Auto-start login flow in browser environment
|
|
1117
1352
|
if (typeof window !== 'undefined') {
|
|
1118
|
-
|
|
1353
|
+
// Default to external-auth if not specified
|
|
1354
|
+
const useExternalAuth = this.config.authMethod !== 'headless';
|
|
1355
|
+
await this.startAuthFlow(useExternalAuth);
|
|
1119
1356
|
// If we reach here, authentication was successful
|
|
1120
1357
|
// If it failed, startAuthFlow() will have thrown an error
|
|
1121
1358
|
}
|
|
@@ -1126,27 +1363,53 @@ class AuthManager extends EventEmitter {
|
|
|
1126
1363
|
}
|
|
1127
1364
|
/**
|
|
1128
1365
|
* Start the authentication flow UI
|
|
1366
|
+
*
|
|
1367
|
+
* @param useExternalAuth - Use external-auth OAuth flow instead of headless flow
|
|
1129
1368
|
*/
|
|
1130
|
-
async startAuthFlow() {
|
|
1131
|
-
var _a;
|
|
1132
|
-
if (this.authFlowManager) {
|
|
1369
|
+
async startAuthFlow(useExternalAuth = false) {
|
|
1370
|
+
var _a, _b;
|
|
1371
|
+
if (this.authFlowManager || this.externalAuthFlowManager) {
|
|
1133
1372
|
// Already in progress
|
|
1134
1373
|
return;
|
|
1135
1374
|
}
|
|
1136
1375
|
try {
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1376
|
+
if (useExternalAuth) {
|
|
1377
|
+
// Use external-auth OAuth popup flow
|
|
1378
|
+
this.externalAuthFlowManager = new ExternalAuthFlowManager(this.baseURL, this.config.gameId);
|
|
1379
|
+
// Get player token directly from external-auth flow
|
|
1380
|
+
const playerToken = await this.externalAuthFlowManager.startFlow();
|
|
1381
|
+
// Update auth state with the player token
|
|
1382
|
+
this.authState = {
|
|
1383
|
+
isAuthenticated: true,
|
|
1384
|
+
token: playerToken,
|
|
1385
|
+
tokenType: 'player',
|
|
1386
|
+
};
|
|
1387
|
+
// Save to storage
|
|
1388
|
+
await this.storage.saveAuthState(this.config.gameId, this.authState);
|
|
1389
|
+
await this.storage.saveSharedToken(playerToken);
|
|
1390
|
+
this.emit('authenticated', this.authState);
|
|
1391
|
+
// Clean up
|
|
1392
|
+
this.externalAuthFlowManager.destroy();
|
|
1393
|
+
this.externalAuthFlowManager = null;
|
|
1394
|
+
}
|
|
1395
|
+
else {
|
|
1396
|
+
// Use headless verification code flow
|
|
1397
|
+
this.authFlowManager = new AuthFlowManager(this.baseURL);
|
|
1398
|
+
// Get global token from auth flow
|
|
1399
|
+
const globalToken = await this.authFlowManager.startFlow();
|
|
1400
|
+
// Exchange for player token
|
|
1401
|
+
await this.exchangeJWT(globalToken);
|
|
1402
|
+
// Clean up
|
|
1403
|
+
this.authFlowManager.destroy();
|
|
1404
|
+
this.authFlowManager = null;
|
|
1405
|
+
}
|
|
1145
1406
|
}
|
|
1146
1407
|
catch (error) {
|
|
1147
1408
|
// User canceled or error occurred
|
|
1148
1409
|
(_a = this.authFlowManager) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
1149
1410
|
this.authFlowManager = null;
|
|
1411
|
+
(_b = this.externalAuthFlowManager) === null || _b === void 0 ? void 0 : _b.destroy();
|
|
1412
|
+
this.externalAuthFlowManager = null;
|
|
1150
1413
|
// Re-emit error
|
|
1151
1414
|
this.emit('error', error);
|
|
1152
1415
|
throw error;
|
|
@@ -2787,5 +3050,5 @@ class PlayKitSDK extends EventEmitter {
|
|
|
2787
3050
|
}
|
|
2788
3051
|
}
|
|
2789
3052
|
|
|
2790
|
-
export { AuthManager, ChatClient, ImageClient, NPCClient, PlayKitSDK, PlayerClient, RechargeManager, StreamParser, TokenStorage, PlayKitSDK as default };
|
|
3053
|
+
export { AuthFlowManager, AuthManager, ChatClient, ExternalAuthFlowManager, ImageClient, NPCClient, PlayKitSDK, PlayerClient, RechargeManager, StreamParser, TokenStorage, PlayKitSDK as default };
|
|
2791
3054
|
//# sourceMappingURL=playkit-sdk.esm.js.map
|