@lvce-editor/auth-worker 1.4.0 → 1.6.0
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/authWorkerMain.js +345 -10
- package/package.json +1 -1
package/dist/authWorkerMain.js
CHANGED
|
@@ -1127,18 +1127,18 @@ const create = rpcId => {
|
|
|
1127
1127
|
};
|
|
1128
1128
|
};
|
|
1129
1129
|
|
|
1130
|
+
const Electron = 2;
|
|
1131
|
+
|
|
1130
1132
|
const OpenerWorker = 4561;
|
|
1131
1133
|
const RendererWorker = 1;
|
|
1132
1134
|
|
|
1133
1135
|
const {
|
|
1134
|
-
invoke,
|
|
1136
|
+
invoke: invoke$1,
|
|
1135
1137
|
set: set$1
|
|
1136
1138
|
} = create(OpenerWorker);
|
|
1137
|
-
const openUrl = async (url, platform) => {
|
|
1138
|
-
return invoke('Open.openUrl', url, platform);
|
|
1139
|
-
};
|
|
1140
1139
|
|
|
1141
1140
|
const {
|
|
1141
|
+
invoke,
|
|
1142
1142
|
invokeAndTransfer,
|
|
1143
1143
|
set
|
|
1144
1144
|
} = create(RendererWorker);
|
|
@@ -1178,10 +1178,6 @@ const getBackendAuthUrl = (backendUrl, path) => {
|
|
|
1178
1178
|
return `${trimTrailingSlashes(backendUrl)}${path}`;
|
|
1179
1179
|
};
|
|
1180
1180
|
|
|
1181
|
-
const getBackendLoginUrl = backendUrl => {
|
|
1182
|
-
return getBackendAuthUrl(backendUrl, '/auth/login');
|
|
1183
|
-
};
|
|
1184
|
-
|
|
1185
1181
|
const getLoggedOutBackendAuthState = (authErrorMessage = '') => {
|
|
1186
1182
|
return {
|
|
1187
1183
|
authAccessToken: '',
|
|
@@ -1359,6 +1355,287 @@ const waitForBackendLogin = async (backendUrl, timeoutMs = 30_000, pollIntervalM
|
|
|
1359
1355
|
return getLoggedOutBackendAuthState(lastErrorMessage);
|
|
1360
1356
|
};
|
|
1361
1357
|
|
|
1358
|
+
const successHtml = `<!doctype html>
|
|
1359
|
+
<html lang="en">
|
|
1360
|
+
<head>
|
|
1361
|
+
<meta charset="utf-8">
|
|
1362
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1363
|
+
<title>Authentication Complete</title>
|
|
1364
|
+
<style>
|
|
1365
|
+
:root {
|
|
1366
|
+
color-scheme: light;
|
|
1367
|
+
--background: linear-gradient(180deg, #f4f7fb 0%, #e9eef8 100%);
|
|
1368
|
+
--panel: rgba(255, 255, 255, 0.92);
|
|
1369
|
+
--panel-border: rgba(33, 52, 88, 0.08);
|
|
1370
|
+
--text: #132238;
|
|
1371
|
+
--muted: #5f6f86;
|
|
1372
|
+
--accent: #1f7a5a;
|
|
1373
|
+
--accent-soft: #e7f6ef;
|
|
1374
|
+
--shadow: 0 24px 60px rgba(44, 65, 98, 0.16);
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
* {
|
|
1378
|
+
box-sizing: border-box;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
html,
|
|
1382
|
+
body {
|
|
1383
|
+
margin: 0;
|
|
1384
|
+
min-height: 100%;
|
|
1385
|
+
font-family: Inter, sans-serif;
|
|
1386
|
+
background: var(--background);
|
|
1387
|
+
color: var(--text);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
body {
|
|
1391
|
+
display: flex;
|
|
1392
|
+
align-items: center;
|
|
1393
|
+
justify-content: center;
|
|
1394
|
+
padding: 24px;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
.card {
|
|
1398
|
+
width: min(100%, 460px);
|
|
1399
|
+
padding: 32px 28px;
|
|
1400
|
+
border: 1px solid var(--panel-border);
|
|
1401
|
+
border-radius: 20px;
|
|
1402
|
+
background: var(--panel);
|
|
1403
|
+
box-shadow: var(--shadow);
|
|
1404
|
+
text-align: center;
|
|
1405
|
+
backdrop-filter: blur(12px);
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
.badge {
|
|
1409
|
+
display: inline-flex;
|
|
1410
|
+
align-items: center;
|
|
1411
|
+
justify-content: center;
|
|
1412
|
+
width: 64px;
|
|
1413
|
+
height: 64px;
|
|
1414
|
+
margin-bottom: 20px;
|
|
1415
|
+
border-radius: 999px;
|
|
1416
|
+
background: var(--accent-soft);
|
|
1417
|
+
color: var(--accent);
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
h1 {
|
|
1421
|
+
margin: 0;
|
|
1422
|
+
font-size: 28px;
|
|
1423
|
+
line-height: 1.15;
|
|
1424
|
+
letter-spacing: -0.03em;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
p {
|
|
1428
|
+
margin: 14px 0 0;
|
|
1429
|
+
font-size: 15px;
|
|
1430
|
+
line-height: 1.6;
|
|
1431
|
+
color: var(--muted);
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
.hint {
|
|
1435
|
+
margin-top: 22px;
|
|
1436
|
+
padding: 12px 14px;
|
|
1437
|
+
border-radius: 12px;
|
|
1438
|
+
background: rgba(19, 34, 56, 0.04);
|
|
1439
|
+
font-size: 14px;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
.button {
|
|
1443
|
+
margin-top: 20px;
|
|
1444
|
+
border: 0;
|
|
1445
|
+
border-radius: 999px;
|
|
1446
|
+
background: var(--text);
|
|
1447
|
+
color: #fff;
|
|
1448
|
+
padding: 10px 18px;
|
|
1449
|
+
font: inherit;
|
|
1450
|
+
cursor: pointer;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
.button:hover {
|
|
1454
|
+
background: #0d182a;
|
|
1455
|
+
}
|
|
1456
|
+
</style>
|
|
1457
|
+
</head>
|
|
1458
|
+
<body>
|
|
1459
|
+
<main class="card">
|
|
1460
|
+
<div class="badge" aria-hidden="true">
|
|
1461
|
+
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" role="presentation">
|
|
1462
|
+
<path d="M20 7L10 17L5 12" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
1463
|
+
</svg>
|
|
1464
|
+
</div>
|
|
1465
|
+
<h1>Authentication complete</h1>
|
|
1466
|
+
<p>Your sign-in finished successfully. You can return to the app now.</p>
|
|
1467
|
+
<p class="hint">This window is no longer needed and can be closed.</p>
|
|
1468
|
+
<button class="button" type="button" id="close-button">Close Window</button>
|
|
1469
|
+
</main>
|
|
1470
|
+
<script>
|
|
1471
|
+
const closeWindow = () => {
|
|
1472
|
+
window.close()
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
document.getElementById('close-button')?.addEventListener('click', closeWindow)
|
|
1476
|
+
window.setTimeout(closeWindow, 1200)
|
|
1477
|
+
</script>
|
|
1478
|
+
</body>
|
|
1479
|
+
</html>`;
|
|
1480
|
+
const errorHtml = `<!doctype html>
|
|
1481
|
+
<html lang="en">
|
|
1482
|
+
<head>
|
|
1483
|
+
<meta charset="utf-8">
|
|
1484
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1485
|
+
<title>Authentication Failed</title>
|
|
1486
|
+
<style>
|
|
1487
|
+
:root {
|
|
1488
|
+
color-scheme: light;
|
|
1489
|
+
--background: linear-gradient(180deg, #fbf4f2 0%, #f7e5df 100%);
|
|
1490
|
+
--panel: rgba(255, 255, 255, 0.94);
|
|
1491
|
+
--panel-border: rgba(120, 43, 24, 0.12);
|
|
1492
|
+
--text: #3a1d16;
|
|
1493
|
+
--muted: #7f5a4c;
|
|
1494
|
+
--accent: #b4492d;
|
|
1495
|
+
--accent-soft: #fde9e2;
|
|
1496
|
+
--shadow: 0 24px 60px rgba(96, 48, 32, 0.14);
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
* {
|
|
1500
|
+
box-sizing: border-box;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
html,
|
|
1504
|
+
body {
|
|
1505
|
+
margin: 0;
|
|
1506
|
+
min-height: 100%;
|
|
1507
|
+
font-family: Inter, sans-serif;
|
|
1508
|
+
background: var(--background);
|
|
1509
|
+
color: var(--text);
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
body {
|
|
1513
|
+
display: flex;
|
|
1514
|
+
align-items: center;
|
|
1515
|
+
justify-content: center;
|
|
1516
|
+
padding: 24px;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
.card {
|
|
1520
|
+
width: min(100%, 460px);
|
|
1521
|
+
padding: 32px 28px;
|
|
1522
|
+
border: 1px solid var(--panel-border);
|
|
1523
|
+
border-radius: 20px;
|
|
1524
|
+
background: var(--panel);
|
|
1525
|
+
box-shadow: var(--shadow);
|
|
1526
|
+
text-align: center;
|
|
1527
|
+
backdrop-filter: blur(12px);
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
.badge {
|
|
1531
|
+
display: inline-flex;
|
|
1532
|
+
align-items: center;
|
|
1533
|
+
justify-content: center;
|
|
1534
|
+
width: 64px;
|
|
1535
|
+
height: 64px;
|
|
1536
|
+
margin-bottom: 20px;
|
|
1537
|
+
border-radius: 999px;
|
|
1538
|
+
background: var(--accent-soft);
|
|
1539
|
+
color: var(--accent);
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
h1 {
|
|
1543
|
+
margin: 0;
|
|
1544
|
+
font-size: 28px;
|
|
1545
|
+
line-height: 1.15;
|
|
1546
|
+
letter-spacing: -0.03em;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
p {
|
|
1550
|
+
margin: 14px 0 0;
|
|
1551
|
+
font-size: 15px;
|
|
1552
|
+
line-height: 1.6;
|
|
1553
|
+
color: var(--muted);
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
.hint {
|
|
1557
|
+
margin-top: 22px;
|
|
1558
|
+
padding: 12px 14px;
|
|
1559
|
+
border-radius: 12px;
|
|
1560
|
+
background: rgba(58, 29, 22, 0.05);
|
|
1561
|
+
font-size: 14px;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
.button {
|
|
1565
|
+
margin-top: 20px;
|
|
1566
|
+
border: 0;
|
|
1567
|
+
border-radius: 999px;
|
|
1568
|
+
background: var(--text);
|
|
1569
|
+
color: #fff;
|
|
1570
|
+
padding: 10px 18px;
|
|
1571
|
+
font: inherit;
|
|
1572
|
+
cursor: pointer;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
.button:hover {
|
|
1576
|
+
background: #24110d;
|
|
1577
|
+
}
|
|
1578
|
+
</style>
|
|
1579
|
+
</head>
|
|
1580
|
+
<body>
|
|
1581
|
+
<main class="card">
|
|
1582
|
+
<div class="badge" aria-hidden="true">
|
|
1583
|
+
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" role="presentation">
|
|
1584
|
+
<path d="M12 8V12" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"></path>
|
|
1585
|
+
<path d="M12 16H12.01" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"></path>
|
|
1586
|
+
<path d="M10.29 3.86L1.82 18A2 2 0 0 0 3.53 21H20.47A2 2 0 0 0 22.18 18L13.71 3.86A2 2 0 0 0 10.29 3.86Z" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
1587
|
+
</svg>
|
|
1588
|
+
</div>
|
|
1589
|
+
<h1>Authentication failed</h1>
|
|
1590
|
+
<p>The sign-in flow did not return an authorization code.</p>
|
|
1591
|
+
<p class="hint">You can close this window and try again from the app.</p>
|
|
1592
|
+
<button class="button" type="button" id="close-button">Close Window</button>
|
|
1593
|
+
</main>
|
|
1594
|
+
<script>
|
|
1595
|
+
const closeWindow = () => {
|
|
1596
|
+
window.close()
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
document.getElementById('close-button')?.addEventListener('click', closeWindow)
|
|
1600
|
+
</script>
|
|
1601
|
+
</body>
|
|
1602
|
+
</html>`;
|
|
1603
|
+
|
|
1604
|
+
const getElectronRedirectUri = async (uid, invoke) => {
|
|
1605
|
+
return `http://localhost:${await invoke('OAuthServer.create', String(uid), successHtml, errorHtml)}`;
|
|
1606
|
+
};
|
|
1607
|
+
const getCurrentHref = async () => {
|
|
1608
|
+
try {
|
|
1609
|
+
return await invoke('Layout.getHref');
|
|
1610
|
+
} catch {
|
|
1611
|
+
// ignore
|
|
1612
|
+
}
|
|
1613
|
+
if (!globalThis.location || typeof globalThis.location.href !== 'string' || !globalThis.location.href) {
|
|
1614
|
+
return '';
|
|
1615
|
+
}
|
|
1616
|
+
return globalThis.location.href;
|
|
1617
|
+
};
|
|
1618
|
+
const getEffectiveRedirectUri = async (platform, uid, redirectUri) => {
|
|
1619
|
+
if (redirectUri) {
|
|
1620
|
+
return redirectUri;
|
|
1621
|
+
}
|
|
1622
|
+
if (platform === Electron) {
|
|
1623
|
+
return getElectronRedirectUri(uid, invoke);
|
|
1624
|
+
}
|
|
1625
|
+
return getCurrentHref();
|
|
1626
|
+
};
|
|
1627
|
+
const getBackendLoginRequest = async (backendUrl, platform = 0, uid = 0, redirectUri = '') => {
|
|
1628
|
+
const loginUrl = new URL(getBackendAuthUrl(backendUrl, '/login'));
|
|
1629
|
+
const effectiveRedirectUri = await getEffectiveRedirectUri(platform, uid, redirectUri);
|
|
1630
|
+
if (effectiveRedirectUri) {
|
|
1631
|
+
loginUrl.searchParams.set('redirect_uri', effectiveRedirectUri);
|
|
1632
|
+
}
|
|
1633
|
+
return {
|
|
1634
|
+
loginUrl: loginUrl.toString(),
|
|
1635
|
+
redirectUri: effectiveRedirectUri
|
|
1636
|
+
};
|
|
1637
|
+
};
|
|
1638
|
+
|
|
1362
1639
|
const getLoggedInState = (state, response) => {
|
|
1363
1640
|
const accessToken = typeof response.accessToken === 'string' ? response.accessToken : '';
|
|
1364
1641
|
return {
|
|
@@ -1379,6 +1656,58 @@ const isLoginResponse = value => {
|
|
|
1379
1656
|
return true;
|
|
1380
1657
|
};
|
|
1381
1658
|
|
|
1659
|
+
const getBackendNativeExchangeUrl = backendUrl => {
|
|
1660
|
+
return getBackendAuthUrl(backendUrl, '/auth/native/exchange');
|
|
1661
|
+
};
|
|
1662
|
+
|
|
1663
|
+
const getExchangeErrorMessage = async response => {
|
|
1664
|
+
try {
|
|
1665
|
+
const payload = await response.json();
|
|
1666
|
+
if (payload && typeof payload === 'object' && typeof payload.error === 'string' && payload.error) {
|
|
1667
|
+
return payload.error;
|
|
1668
|
+
}
|
|
1669
|
+
} catch {
|
|
1670
|
+
// ignore
|
|
1671
|
+
}
|
|
1672
|
+
return 'Backend authentication failed.';
|
|
1673
|
+
};
|
|
1674
|
+
const exchangeElectronAuthorizationCode = async (backendUrl, code, redirectUri) => {
|
|
1675
|
+
const response = await fetch(getBackendNativeExchangeUrl(backendUrl), {
|
|
1676
|
+
body: JSON.stringify({
|
|
1677
|
+
code,
|
|
1678
|
+
redirectUri
|
|
1679
|
+
}),
|
|
1680
|
+
credentials: 'include',
|
|
1681
|
+
headers: {
|
|
1682
|
+
Accept: 'application/json',
|
|
1683
|
+
'Content-Type': 'application/json'
|
|
1684
|
+
},
|
|
1685
|
+
method: 'POST'
|
|
1686
|
+
});
|
|
1687
|
+
if (!response.ok) {
|
|
1688
|
+
throw new Error(await getExchangeErrorMessage(response));
|
|
1689
|
+
}
|
|
1690
|
+
};
|
|
1691
|
+
|
|
1692
|
+
const hasAuthorizationCode = value => {
|
|
1693
|
+
return typeof value === 'string' && value.length > 0;
|
|
1694
|
+
};
|
|
1695
|
+
const waitForElectronBackendLogin = async (backendUrl, uid, redirectUri, timeoutMs = 30_000, pollIntervalMs = 1000) => {
|
|
1696
|
+
const started = Date.now();
|
|
1697
|
+
const deadline = started + timeoutMs;
|
|
1698
|
+
while (Date.now() < deadline) {
|
|
1699
|
+
const authorizationCode = await invoke('OAuthServer.getCode', String(uid));
|
|
1700
|
+
if (hasAuthorizationCode(authorizationCode)) {
|
|
1701
|
+
const elapsed = Date.now() - started;
|
|
1702
|
+
const remainingTime = Math.max(0, timeoutMs - elapsed);
|
|
1703
|
+
await exchangeElectronAuthorizationCode(backendUrl, authorizationCode, redirectUri);
|
|
1704
|
+
return waitForBackendLogin(backendUrl, remainingTime, pollIntervalMs);
|
|
1705
|
+
}
|
|
1706
|
+
await delay(pollIntervalMs);
|
|
1707
|
+
}
|
|
1708
|
+
return getLoggedOutBackendAuthState('Timed out waiting for backend login.');
|
|
1709
|
+
};
|
|
1710
|
+
|
|
1382
1711
|
const handleClickLogin = async options => {
|
|
1383
1712
|
const {
|
|
1384
1713
|
backendUrl,
|
|
@@ -1411,8 +1740,14 @@ const handleClickLogin = async options => {
|
|
|
1411
1740
|
}
|
|
1412
1741
|
return getLoggedInState(signingInState, response);
|
|
1413
1742
|
}
|
|
1414
|
-
|
|
1415
|
-
const
|
|
1743
|
+
const uid = 0;
|
|
1744
|
+
const {
|
|
1745
|
+
loginUrl,
|
|
1746
|
+
redirectUri
|
|
1747
|
+
} = await getBackendLoginRequest(backendUrl, platform, uid);
|
|
1748
|
+
const authUseRedirect = false;
|
|
1749
|
+
await invoke$1('Open.openUrl', loginUrl, platform, authUseRedirect);
|
|
1750
|
+
const authState = platform === Electron ? await waitForElectronBackendLogin(backendUrl, uid, redirectUri) : await waitForBackendLogin(backendUrl);
|
|
1416
1751
|
return {
|
|
1417
1752
|
...authState
|
|
1418
1753
|
};
|