@seaverse/auth-sdk 0.2.0 → 0.2.2
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/README.md +136 -87
- package/dist/auth-modal.css +1 -1
- package/dist/index.cjs +158 -203
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +75 -53
- package/dist/index.js +158 -203
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1042,7 +1042,7 @@ class AuthFactory {
|
|
|
1042
1042
|
const ENVIRONMENT_CONFIGS = {
|
|
1043
1043
|
production: {
|
|
1044
1044
|
name: 'production',
|
|
1045
|
-
baseURL: 'https://
|
|
1045
|
+
baseURL: 'https://account-hub.seaverse.ai',
|
|
1046
1046
|
wsURL: 'wss://api.seaverse.com',
|
|
1047
1047
|
isProduction: true,
|
|
1048
1048
|
},
|
|
@@ -1226,6 +1226,31 @@ class SeaVerseBackendAPIClient {
|
|
|
1226
1226
|
],
|
|
1227
1227
|
};
|
|
1228
1228
|
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Set authentication token
|
|
1231
|
+
* Update the client's authentication token (useful after OAuth login)
|
|
1232
|
+
*
|
|
1233
|
+
* @param token - JWT token to set
|
|
1234
|
+
*
|
|
1235
|
+
* @example
|
|
1236
|
+
* // After OAuth login
|
|
1237
|
+
* const token = new URLSearchParams(window.location.search).get('token');
|
|
1238
|
+
* if (token) {
|
|
1239
|
+
* client.setToken(token);
|
|
1240
|
+
* localStorage.setItem('token', token);
|
|
1241
|
+
* }
|
|
1242
|
+
*/
|
|
1243
|
+
setToken(token) {
|
|
1244
|
+
const auth = AuthFactory.create({
|
|
1245
|
+
type: 'jwt',
|
|
1246
|
+
credentials: {
|
|
1247
|
+
type: 'jwt',
|
|
1248
|
+
token,
|
|
1249
|
+
},
|
|
1250
|
+
});
|
|
1251
|
+
// Update the httpClient's auth
|
|
1252
|
+
this.httpClient.auth = auth;
|
|
1253
|
+
}
|
|
1229
1254
|
// ============================================================================
|
|
1230
1255
|
// Authentication & OAuth APIs
|
|
1231
1256
|
// ============================================================================
|
|
@@ -1373,36 +1398,39 @@ class SeaVerseBackendAPIClient {
|
|
|
1373
1398
|
// OAuth APIs
|
|
1374
1399
|
// ============================================================================
|
|
1375
1400
|
/**
|
|
1376
|
-
*
|
|
1377
|
-
*
|
|
1378
|
-
|
|
1379
|
-
|
|
1401
|
+
* Google OAuth authorization (Backend Proxy Mode)
|
|
1402
|
+
* Generate OAuth authorization URL for Google login
|
|
1403
|
+
*
|
|
1404
|
+
* @param data - OAuth authorize request (return_url is optional, defaults to window.location.origin)
|
|
1405
|
+
* @param options - Additional axios request options
|
|
1406
|
+
*
|
|
1407
|
+
* @example
|
|
1408
|
+
* // 使用默认 return_url (当前页面)
|
|
1409
|
+
* const { authorize_url } = await client.googleAuthorize();
|
|
1410
|
+
* window.location.href = authorize_url;
|
|
1411
|
+
*
|
|
1412
|
+
* @example
|
|
1413
|
+
* // 自定义 return_url
|
|
1414
|
+
* const { authorize_url } = await client.googleAuthorize({
|
|
1415
|
+
* return_url: 'https://mygame.com/dashboard'
|
|
1416
|
+
* });
|
|
1417
|
+
* window.location.href = authorize_url;
|
|
1418
|
+
*/
|
|
1419
|
+
async googleAuthorize(data, options) {
|
|
1420
|
+
// 如果没有传 return_url,使用当前页面地址
|
|
1421
|
+
const return_url = data?.return_url || (typeof window !== 'undefined' ? window.location.origin : '');
|
|
1380
1422
|
const config = {
|
|
1381
1423
|
method: 'POST',
|
|
1382
|
-
url: `/sdk/v1/auth/google/
|
|
1383
|
-
data,
|
|
1424
|
+
url: `/sdk/v1/auth/google/authorize`,
|
|
1425
|
+
data: { return_url },
|
|
1384
1426
|
headers: {
|
|
1385
|
-
'X-Operation-Id': '
|
|
1427
|
+
'X-Operation-Id': 'googleAuthorize',
|
|
1386
1428
|
...options?.headers,
|
|
1387
1429
|
},
|
|
1388
1430
|
...options,
|
|
1389
1431
|
};
|
|
1390
1432
|
const response = await this.httpClient.request(config);
|
|
1391
|
-
|
|
1392
|
-
const apiData = response.data?.data || response.data;
|
|
1393
|
-
if (!apiData || !apiData.token) {
|
|
1394
|
-
throw new Error('No token in API response');
|
|
1395
|
-
}
|
|
1396
|
-
return {
|
|
1397
|
-
token: apiData.token,
|
|
1398
|
-
user: {
|
|
1399
|
-
id: apiData.tempUserId || apiData.userId || apiData.id,
|
|
1400
|
-
email: apiData.email,
|
|
1401
|
-
username: apiData.username,
|
|
1402
|
-
...(apiData.provider && { provider: apiData.provider }),
|
|
1403
|
-
...(apiData.requiresInvitationCode !== undefined && { requiresInvitationCode: apiData.requiresInvitationCode }),
|
|
1404
|
-
}
|
|
1405
|
-
};
|
|
1433
|
+
return response.data;
|
|
1406
1434
|
}
|
|
1407
1435
|
/**
|
|
1408
1436
|
* Unlink Google account
|
|
@@ -1422,35 +1450,27 @@ class SeaVerseBackendAPIClient {
|
|
|
1422
1450
|
return response.data;
|
|
1423
1451
|
}
|
|
1424
1452
|
/**
|
|
1425
|
-
*
|
|
1453
|
+
* Discord OAuth authorization (Backend Proxy Mode)
|
|
1454
|
+
* Generate OAuth authorization URL for Discord login
|
|
1455
|
+
*
|
|
1456
|
+
* @param data - OAuth authorize request (return_url is optional, defaults to window.location.origin)
|
|
1457
|
+
* @param options - Additional axios request options
|
|
1426
1458
|
*/
|
|
1427
|
-
async
|
|
1459
|
+
async discordAuthorize(data, options) {
|
|
1460
|
+
// 如果没有传 return_url,使用当前页面地址
|
|
1461
|
+
const return_url = data?.return_url || (typeof window !== 'undefined' ? window.location.origin : '');
|
|
1428
1462
|
const config = {
|
|
1429
1463
|
method: 'POST',
|
|
1430
|
-
url: `/sdk/v1/auth/discord/
|
|
1431
|
-
data,
|
|
1464
|
+
url: `/sdk/v1/auth/discord/authorize`,
|
|
1465
|
+
data: { return_url },
|
|
1432
1466
|
headers: {
|
|
1433
|
-
'X-Operation-Id': '
|
|
1467
|
+
'X-Operation-Id': 'discordAuthorize',
|
|
1434
1468
|
...options?.headers,
|
|
1435
1469
|
},
|
|
1436
1470
|
...options,
|
|
1437
1471
|
};
|
|
1438
1472
|
const response = await this.httpClient.request(config);
|
|
1439
|
-
|
|
1440
|
-
const apiData = response.data?.data || response.data;
|
|
1441
|
-
if (!apiData || !apiData.token) {
|
|
1442
|
-
throw new Error('No token in API response');
|
|
1443
|
-
}
|
|
1444
|
-
return {
|
|
1445
|
-
token: apiData.token,
|
|
1446
|
-
user: {
|
|
1447
|
-
id: apiData.tempUserId || apiData.userId || apiData.id,
|
|
1448
|
-
email: apiData.email,
|
|
1449
|
-
username: apiData.username,
|
|
1450
|
-
...(apiData.provider && { provider: apiData.provider }),
|
|
1451
|
-
...(apiData.requiresInvitationCode !== undefined && { requiresInvitationCode: apiData.requiresInvitationCode }),
|
|
1452
|
-
}
|
|
1453
|
-
};
|
|
1473
|
+
return response.data;
|
|
1454
1474
|
}
|
|
1455
1475
|
/**
|
|
1456
1476
|
* Unlink Discord account
|
|
@@ -1469,46 +1489,27 @@ class SeaVerseBackendAPIClient {
|
|
|
1469
1489
|
return response.data;
|
|
1470
1490
|
}
|
|
1471
1491
|
/**
|
|
1472
|
-
*
|
|
1492
|
+
* GitHub OAuth authorization (Backend Proxy Mode)
|
|
1493
|
+
* Generate OAuth authorization URL for GitHub login
|
|
1494
|
+
*
|
|
1495
|
+
* @param data - OAuth authorize request (return_url is optional, defaults to window.location.origin)
|
|
1496
|
+
* @param options - Additional axios request options
|
|
1473
1497
|
*/
|
|
1474
|
-
async
|
|
1498
|
+
async githubAuthorize(data, options) {
|
|
1499
|
+
// 如果没有传 return_url,使用当前页面地址
|
|
1500
|
+
const return_url = data?.return_url || (typeof window !== 'undefined' ? window.location.origin : '');
|
|
1475
1501
|
const config = {
|
|
1476
1502
|
method: 'POST',
|
|
1477
|
-
url: `/sdk/v1/auth/github/
|
|
1478
|
-
data,
|
|
1503
|
+
url: `/sdk/v1/auth/github/authorize`,
|
|
1504
|
+
data: { return_url },
|
|
1479
1505
|
headers: {
|
|
1480
|
-
'X-Operation-Id': '
|
|
1506
|
+
'X-Operation-Id': 'githubAuthorize',
|
|
1481
1507
|
...options?.headers,
|
|
1482
1508
|
},
|
|
1483
1509
|
...options,
|
|
1484
1510
|
};
|
|
1485
|
-
console.log('🔐 [SDK Client] Calling githubCodeToToken with config:', config);
|
|
1486
1511
|
const response = await this.httpClient.request(config);
|
|
1487
|
-
|
|
1488
|
-
// ✅ API返回格式: { data: { token, tempUserId, email, ... }, success: true }
|
|
1489
|
-
// 需要访问 response.data.data 而不是 response.data
|
|
1490
|
-
const apiData = response.data?.data || response.data;
|
|
1491
|
-
console.log('📡 [SDK Client] Extracted apiData:', apiData);
|
|
1492
|
-
console.log('📡 [SDK Client] apiData.token:', apiData?.token);
|
|
1493
|
-
if (!apiData || !apiData.token) {
|
|
1494
|
-
console.error('❌ [SDK Client] No token found!');
|
|
1495
|
-
console.error('❌ [SDK Client] response.data:', JSON.stringify(response.data, null, 2));
|
|
1496
|
-
throw new Error('No token in API response');
|
|
1497
|
-
}
|
|
1498
|
-
// 构造符合SDK期望的响应格式
|
|
1499
|
-
const normalizedResponse = {
|
|
1500
|
-
token: apiData.token,
|
|
1501
|
-
user: {
|
|
1502
|
-
id: apiData.tempUserId || apiData.userId || apiData.id,
|
|
1503
|
-
email: apiData.email,
|
|
1504
|
-
username: apiData.username,
|
|
1505
|
-
// 保留额外的字段
|
|
1506
|
-
...(apiData.provider && { provider: apiData.provider }),
|
|
1507
|
-
...(apiData.requiresInvitationCode !== undefined && { requiresInvitationCode: apiData.requiresInvitationCode }),
|
|
1508
|
-
}
|
|
1509
|
-
};
|
|
1510
|
-
console.log('✅ [SDK Client] Normalized response:', normalizedResponse);
|
|
1511
|
-
return normalizedResponse;
|
|
1512
|
+
return response.data;
|
|
1512
1513
|
}
|
|
1513
1514
|
/**
|
|
1514
1515
|
* Unlink GitHub account
|
|
@@ -1962,9 +1963,9 @@ class AuthModal {
|
|
|
1962
1963
|
submitBtn.appendChild(btnText);
|
|
1963
1964
|
submitBtn.appendChild(btnLoader);
|
|
1964
1965
|
form.appendChild(submitBtn);
|
|
1965
|
-
// OAuth buttons - only show if
|
|
1966
|
-
const hasOAuth = this.options.
|
|
1967
|
-
(this.options.
|
|
1966
|
+
// OAuth buttons - only show if enabled
|
|
1967
|
+
const hasOAuth = this.options.enableOAuth &&
|
|
1968
|
+
(this.options.enableOAuth.google || this.options.enableOAuth.discord || this.options.enableOAuth.github);
|
|
1968
1969
|
if (hasOAuth) {
|
|
1969
1970
|
// Divider
|
|
1970
1971
|
const divider = document.createElement('div');
|
|
@@ -1974,13 +1975,13 @@ class AuthModal {
|
|
|
1974
1975
|
// Social buttons grid (Google + GitHub)
|
|
1975
1976
|
const socialGrid = document.createElement('div');
|
|
1976
1977
|
socialGrid.className = 'social-buttons-grid';
|
|
1977
|
-
// Google button - only if
|
|
1978
|
-
if (this.options.
|
|
1978
|
+
// Google button - only if enabled
|
|
1979
|
+
if (this.options.enableOAuth?.google) {
|
|
1979
1980
|
const googleBtn = this.createSocialButton('google', 'Google', 'login');
|
|
1980
1981
|
socialGrid.appendChild(googleBtn);
|
|
1981
1982
|
}
|
|
1982
1983
|
// GitHub button - only if configured
|
|
1983
|
-
if (this.options.
|
|
1984
|
+
if (this.options.enableOAuth?.github) {
|
|
1984
1985
|
const githubBtn = this.createSocialButton('github', 'Github', 'login');
|
|
1985
1986
|
socialGrid.appendChild(githubBtn);
|
|
1986
1987
|
}
|
|
@@ -1989,7 +1990,7 @@ class AuthModal {
|
|
|
1989
1990
|
form.appendChild(socialGrid);
|
|
1990
1991
|
}
|
|
1991
1992
|
// Discord button (full width) - only if configured
|
|
1992
|
-
if (this.options.
|
|
1993
|
+
if (this.options.enableOAuth?.discord) {
|
|
1993
1994
|
const discordBtn = this.createSocialButton('discord', 'Discord', 'login', true);
|
|
1994
1995
|
form.appendChild(discordBtn);
|
|
1995
1996
|
}
|
|
@@ -2066,24 +2067,38 @@ class AuthModal {
|
|
|
2066
2067
|
submitBtn.appendChild(btnText);
|
|
2067
2068
|
submitBtn.appendChild(btnLoader);
|
|
2068
2069
|
form.appendChild(submitBtn);
|
|
2069
|
-
//
|
|
2070
|
-
const
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2070
|
+
// OAuth buttons - only show if enabled
|
|
2071
|
+
const hasOAuth = this.options.enableOAuth &&
|
|
2072
|
+
(this.options.enableOAuth.google || this.options.enableOAuth.discord || this.options.enableOAuth.github);
|
|
2073
|
+
if (hasOAuth) {
|
|
2074
|
+
// Divider
|
|
2075
|
+
const divider = document.createElement('div');
|
|
2076
|
+
divider.className = 'divider';
|
|
2077
|
+
divider.textContent = 'OR SIGN UP WITH';
|
|
2078
|
+
form.appendChild(divider);
|
|
2079
|
+
// Social buttons grid (Google + GitHub)
|
|
2080
|
+
const socialGrid = document.createElement('div');
|
|
2081
|
+
socialGrid.className = 'social-buttons-grid';
|
|
2082
|
+
// Google button - only if enabled
|
|
2083
|
+
if (this.options.enableOAuth?.google) {
|
|
2084
|
+
const googleBtn = this.createSocialButton('google', 'Google', 'signup');
|
|
2085
|
+
socialGrid.appendChild(googleBtn);
|
|
2086
|
+
}
|
|
2087
|
+
// GitHub button - only if configured
|
|
2088
|
+
if (this.options.enableOAuth?.github) {
|
|
2089
|
+
const githubBtn = this.createSocialButton('github', 'Github', 'signup');
|
|
2090
|
+
socialGrid.appendChild(githubBtn);
|
|
2091
|
+
}
|
|
2092
|
+
// Only add grid if it has buttons
|
|
2093
|
+
if (socialGrid.children.length > 0) {
|
|
2094
|
+
form.appendChild(socialGrid);
|
|
2095
|
+
}
|
|
2096
|
+
// Discord button (full width) - only if configured
|
|
2097
|
+
if (this.options.enableOAuth?.discord) {
|
|
2098
|
+
const discordBtn = this.createSocialButton('discord', 'Discord', 'signup', true);
|
|
2099
|
+
form.appendChild(discordBtn);
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2087
2102
|
container.appendChild(form);
|
|
2088
2103
|
return container;
|
|
2089
2104
|
}
|
|
@@ -2499,134 +2514,74 @@ class AuthModal {
|
|
|
2499
2514
|
// OAuth Methods
|
|
2500
2515
|
// ============================================================================
|
|
2501
2516
|
/**
|
|
2502
|
-
* Start OAuth flow for a given provider
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
// Store the current state (to verify callback)
|
|
2511
|
-
// ✅ 使用 localStorage 而不是 sessionStorage,避免跨页面跳转时丢失
|
|
2512
|
-
const state = this.generateRandomState();
|
|
2513
|
-
localStorage.setItem('oauth_state', state);
|
|
2514
|
-
localStorage.setItem('oauth_provider', provider);
|
|
2515
|
-
// Build authorization URL
|
|
2516
|
-
const authUrl = this.buildAuthUrl(provider, config, state);
|
|
2517
|
-
// Redirect to OAuth provider
|
|
2518
|
-
window.location.href = authUrl;
|
|
2519
|
-
}
|
|
2520
|
-
/**
|
|
2521
|
-
* Generate random state for CSRF protection
|
|
2522
|
-
*/
|
|
2523
|
-
generateRandomState() {
|
|
2524
|
-
const array = new Uint8Array(32);
|
|
2525
|
-
crypto.getRandomValues(array);
|
|
2526
|
-
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
2527
|
-
}
|
|
2528
|
-
/**
|
|
2529
|
-
* Build authorization URL for each provider
|
|
2517
|
+
* Start OAuth flow for a given provider (Backend Proxy Mode)
|
|
2518
|
+
*
|
|
2519
|
+
* This method calls the backend /authorize endpoint to get the OAuth URL,
|
|
2520
|
+
* then redirects the user. The backend handles:
|
|
2521
|
+
* - State token generation and storage
|
|
2522
|
+
* - OAuth callback processing
|
|
2523
|
+
* - JWT token generation
|
|
2524
|
+
* - Redirecting back to returnUrl with token
|
|
2530
2525
|
*/
|
|
2531
|
-
|
|
2532
|
-
const params = new URLSearchParams({
|
|
2533
|
-
client_id: config.clientId,
|
|
2534
|
-
redirect_uri: config.redirectUri,
|
|
2535
|
-
state,
|
|
2536
|
-
response_type: 'code',
|
|
2537
|
-
});
|
|
2538
|
-
// Provider-specific configurations
|
|
2539
|
-
switch (provider) {
|
|
2540
|
-
case 'google':
|
|
2541
|
-
params.append('scope', config.scope || 'openid email profile');
|
|
2542
|
-
params.append('access_type', 'offline');
|
|
2543
|
-
params.append('prompt', 'consent');
|
|
2544
|
-
return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
|
|
2545
|
-
case 'discord':
|
|
2546
|
-
params.append('scope', config.scope || 'identify email');
|
|
2547
|
-
return `https://discord.com/api/oauth2/authorize?${params.toString()}`;
|
|
2548
|
-
case 'github':
|
|
2549
|
-
params.append('scope', config.scope || 'read:user user:email');
|
|
2550
|
-
return `https://github.com/login/oauth/authorize?${params.toString()}`;
|
|
2551
|
-
default:
|
|
2552
|
-
throw new Error(`Unknown provider: ${provider}`);
|
|
2553
|
-
}
|
|
2554
|
-
}
|
|
2555
|
-
/**
|
|
2556
|
-
* Handle OAuth callback
|
|
2557
|
-
* Call this method from your redirect page with the code and state from URL
|
|
2558
|
-
*/
|
|
2559
|
-
async handleOAuthCallback(code, state) {
|
|
2526
|
+
async startOAuthFlow(provider) {
|
|
2560
2527
|
try {
|
|
2561
|
-
//
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
if (!storedState || storedState !== state) {
|
|
2566
|
-
throw new Error('Invalid state parameter - possible CSRF attack');
|
|
2567
|
-
}
|
|
2568
|
-
if (!provider) {
|
|
2569
|
-
throw new Error('No provider stored in session');
|
|
2570
|
-
}
|
|
2571
|
-
// Clear stored state
|
|
2572
|
-
localStorage.removeItem('oauth_state');
|
|
2573
|
-
localStorage.removeItem('oauth_provider');
|
|
2574
|
-
// Exchange code for token using the appropriate SDK method
|
|
2575
|
-
let response;
|
|
2528
|
+
// Get the return URL (where user should be redirected after OAuth)
|
|
2529
|
+
const return_url = this.options.returnUrl || window.location.origin;
|
|
2530
|
+
// Call backend to get OAuth authorization URL
|
|
2531
|
+
let authorizeUrl;
|
|
2576
2532
|
switch (provider) {
|
|
2577
2533
|
case 'google':
|
|
2578
|
-
|
|
2534
|
+
const googleResult = await this.client.googleAuthorize({ return_url });
|
|
2535
|
+
authorizeUrl = googleResult.authorize_url;
|
|
2579
2536
|
break;
|
|
2580
2537
|
case 'discord':
|
|
2581
|
-
|
|
2538
|
+
const discordResult = await this.client.discordAuthorize({ return_url });
|
|
2539
|
+
authorizeUrl = discordResult.authorize_url;
|
|
2582
2540
|
break;
|
|
2583
2541
|
case 'github':
|
|
2584
|
-
|
|
2542
|
+
const githubResult = await this.client.githubAuthorize({ return_url });
|
|
2543
|
+
authorizeUrl = githubResult.authorize_url;
|
|
2585
2544
|
break;
|
|
2586
2545
|
default:
|
|
2587
2546
|
throw new Error(`Unknown provider: ${provider}`);
|
|
2588
2547
|
}
|
|
2589
|
-
//
|
|
2590
|
-
|
|
2591
|
-
if (this.options.onLoginSuccess) {
|
|
2592
|
-
this.options.onLoginSuccess(response.token, response.user);
|
|
2593
|
-
}
|
|
2594
|
-
return {
|
|
2595
|
-
success: true,
|
|
2596
|
-
token: response.token,
|
|
2597
|
-
user: response.user,
|
|
2598
|
-
};
|
|
2599
|
-
}
|
|
2600
|
-
else {
|
|
2601
|
-
throw new Error('No token received from server');
|
|
2602
|
-
}
|
|
2548
|
+
// Redirect to OAuth provider
|
|
2549
|
+
window.location.href = authorizeUrl;
|
|
2603
2550
|
}
|
|
2604
2551
|
catch (error) {
|
|
2605
|
-
const err = error instanceof Error ? error : new Error(
|
|
2552
|
+
const err = error instanceof Error ? error : new Error(`${provider} OAuth failed`);
|
|
2553
|
+
this.showError(err.message);
|
|
2606
2554
|
if (this.options.onError) {
|
|
2607
2555
|
this.options.onError(err);
|
|
2608
2556
|
}
|
|
2609
|
-
return {
|
|
2610
|
-
success: false,
|
|
2611
|
-
error: err,
|
|
2612
|
-
};
|
|
2613
2557
|
}
|
|
2614
2558
|
}
|
|
2615
2559
|
/**
|
|
2616
|
-
*
|
|
2560
|
+
* Handle OAuth callback (Backend Proxy Mode)
|
|
2561
|
+
*
|
|
2562
|
+
* In Backend Proxy Mode, the backend redirects to returnUrl with token in query param.
|
|
2563
|
+
* This static method checks if current URL has a token and processes it.
|
|
2617
2564
|
*/
|
|
2618
|
-
static
|
|
2565
|
+
static handleOAuthCallback(options) {
|
|
2619
2566
|
const urlParams = new URLSearchParams(window.location.search);
|
|
2620
|
-
const
|
|
2621
|
-
|
|
2622
|
-
if (!code || !state) {
|
|
2567
|
+
const token = urlParams.get('token');
|
|
2568
|
+
if (!token) {
|
|
2623
2569
|
return null; // Not an OAuth callback
|
|
2624
2570
|
}
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
});
|
|
2629
|
-
|
|
2571
|
+
// Clean up URL (remove token from query string)
|
|
2572
|
+
const url = new URL(window.location.href);
|
|
2573
|
+
url.searchParams.delete('token');
|
|
2574
|
+
window.history.replaceState({}, document.title, url.toString());
|
|
2575
|
+
// Automatically set token in client for subsequent authenticated requests
|
|
2576
|
+
options.client.setToken(token);
|
|
2577
|
+
// Call success callback
|
|
2578
|
+
if (options.onLoginSuccess) {
|
|
2579
|
+
options.onLoginSuccess(token, {}); // User info can be fetched separately if needed
|
|
2580
|
+
}
|
|
2581
|
+
return {
|
|
2582
|
+
success: true,
|
|
2583
|
+
token,
|
|
2584
|
+
};
|
|
2630
2585
|
}
|
|
2631
2586
|
}
|
|
2632
2587
|
/**
|