@lvce-editor/auth-worker 1.12.0 → 1.13.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 +145 -47
- package/package.json +1 -1
package/dist/authWorkerMain.js
CHANGED
|
@@ -1261,6 +1261,115 @@ const waitForBackendLogin = async (backendUrl, timeoutMs = 30_000, pollIntervalM
|
|
|
1261
1261
|
return getLoggedOutBackendAuthState(lastErrorMessage);
|
|
1262
1262
|
};
|
|
1263
1263
|
|
|
1264
|
+
if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) ;
|
|
1265
|
+
const ERR_INVALID_ARG_VALUE = 'ERR_INVALID_ARG_VALUE';
|
|
1266
|
+
const ERR_INVALID_ARG_TYPE = 'ERR_INVALID_ARG_TYPE';
|
|
1267
|
+
function CodedTypeError(message, code, cause) {
|
|
1268
|
+
const err = new TypeError(message, {
|
|
1269
|
+
cause
|
|
1270
|
+
});
|
|
1271
|
+
Object.assign(err, {
|
|
1272
|
+
code
|
|
1273
|
+
});
|
|
1274
|
+
return err;
|
|
1275
|
+
}
|
|
1276
|
+
const encoder = new TextEncoder();
|
|
1277
|
+
const decoder = new TextDecoder();
|
|
1278
|
+
function buf(input) {
|
|
1279
|
+
if (typeof input === 'string') {
|
|
1280
|
+
return encoder.encode(input);
|
|
1281
|
+
}
|
|
1282
|
+
return decoder.decode(input);
|
|
1283
|
+
}
|
|
1284
|
+
let encodeBase64Url;
|
|
1285
|
+
if (Uint8Array.prototype.toBase64) {
|
|
1286
|
+
encodeBase64Url = input => {
|
|
1287
|
+
if (input instanceof ArrayBuffer) {
|
|
1288
|
+
input = new Uint8Array(input);
|
|
1289
|
+
}
|
|
1290
|
+
return input.toBase64({
|
|
1291
|
+
alphabet: 'base64url',
|
|
1292
|
+
omitPadding: true
|
|
1293
|
+
});
|
|
1294
|
+
};
|
|
1295
|
+
} else {
|
|
1296
|
+
const CHUNK_SIZE = 0x8000;
|
|
1297
|
+
encodeBase64Url = input => {
|
|
1298
|
+
if (input instanceof ArrayBuffer) {
|
|
1299
|
+
input = new Uint8Array(input);
|
|
1300
|
+
}
|
|
1301
|
+
const arr = [];
|
|
1302
|
+
for (let i = 0; i < input.byteLength; i += CHUNK_SIZE) {
|
|
1303
|
+
arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));
|
|
1304
|
+
}
|
|
1305
|
+
return btoa(arr.join('')).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
let decodeBase64Url;
|
|
1309
|
+
if (Uint8Array.fromBase64) {
|
|
1310
|
+
decodeBase64Url = input => {
|
|
1311
|
+
try {
|
|
1312
|
+
return Uint8Array.fromBase64(input, {
|
|
1313
|
+
alphabet: 'base64url'
|
|
1314
|
+
});
|
|
1315
|
+
} catch (cause) {
|
|
1316
|
+
throw CodedTypeError('The input to be decoded is not correctly encoded.', ERR_INVALID_ARG_VALUE, cause);
|
|
1317
|
+
}
|
|
1318
|
+
};
|
|
1319
|
+
} else {
|
|
1320
|
+
decodeBase64Url = input => {
|
|
1321
|
+
try {
|
|
1322
|
+
const binary = atob(input.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, ''));
|
|
1323
|
+
const bytes = new Uint8Array(binary.length);
|
|
1324
|
+
for (let i = 0; i < binary.length; i++) {
|
|
1325
|
+
bytes[i] = binary.charCodeAt(i);
|
|
1326
|
+
}
|
|
1327
|
+
return bytes;
|
|
1328
|
+
} catch (cause) {
|
|
1329
|
+
throw CodedTypeError('The input to be decoded is not correctly encoded.', ERR_INVALID_ARG_VALUE, cause);
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
function b64u(input) {
|
|
1334
|
+
if (typeof input === 'string') {
|
|
1335
|
+
return decodeBase64Url(input);
|
|
1336
|
+
}
|
|
1337
|
+
return encodeBase64Url(input);
|
|
1338
|
+
}
|
|
1339
|
+
function assertString(input, it, code, cause) {
|
|
1340
|
+
try {
|
|
1341
|
+
if (typeof input !== 'string') {
|
|
1342
|
+
throw CodedTypeError(`${it} must be a string`, ERR_INVALID_ARG_TYPE, cause);
|
|
1343
|
+
}
|
|
1344
|
+
if (input.length === 0) {
|
|
1345
|
+
throw CodedTypeError(`${it} must not be empty`, ERR_INVALID_ARG_VALUE, cause);
|
|
1346
|
+
}
|
|
1347
|
+
} catch (err) {
|
|
1348
|
+
throw err;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
function randomBytes() {
|
|
1352
|
+
return b64u(crypto.getRandomValues(new Uint8Array(32)));
|
|
1353
|
+
}
|
|
1354
|
+
function generateRandomCodeVerifier() {
|
|
1355
|
+
return randomBytes();
|
|
1356
|
+
}
|
|
1357
|
+
async function calculatePKCECodeChallenge(codeVerifier) {
|
|
1358
|
+
assertString(codeVerifier, 'codeVerifier');
|
|
1359
|
+
return b64u(await crypto.subtle.digest('SHA-256', buf(codeVerifier)));
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// cspell:ignore pkce
|
|
1363
|
+
|
|
1364
|
+
const createPkceValues = async () => {
|
|
1365
|
+
const codeVerifier = generateRandomCodeVerifier();
|
|
1366
|
+
const codeChallenge = await calculatePKCECodeChallenge(codeVerifier);
|
|
1367
|
+
return {
|
|
1368
|
+
codeChallenge,
|
|
1369
|
+
codeVerifier
|
|
1370
|
+
};
|
|
1371
|
+
};
|
|
1372
|
+
|
|
1264
1373
|
const getCurrentHref = async () => {
|
|
1265
1374
|
try {
|
|
1266
1375
|
return await invoke('Layout.getHref');
|
|
@@ -1521,7 +1630,7 @@ const errorHtml = `<!doctype html>
|
|
|
1521
1630
|
|
|
1522
1631
|
const getElectronRedirectUri = async uid => {
|
|
1523
1632
|
const localOauthServerPort = await invoke$2('OAuthServer.create', String(uid), successHtml, errorHtml);
|
|
1524
|
-
return `http://localhost:${localOauthServerPort}`;
|
|
1633
|
+
return `http://localhost:${localOauthServerPort}/callback`;
|
|
1525
1634
|
};
|
|
1526
1635
|
|
|
1527
1636
|
const getEffectiveRedirectUri = async (platform, uid, redirectUri) => {
|
|
@@ -1534,13 +1643,31 @@ const getEffectiveRedirectUri = async (platform, uid, redirectUri) => {
|
|
|
1534
1643
|
return getCurrentHref();
|
|
1535
1644
|
};
|
|
1536
1645
|
|
|
1537
|
-
const
|
|
1538
|
-
|
|
1646
|
+
const oidcClientId = 'lvce-editor-native';
|
|
1647
|
+
const oidcScope = 'openid offline_access profile email';
|
|
1648
|
+
|
|
1649
|
+
// cspell:ignore pkce
|
|
1650
|
+
|
|
1651
|
+
const getBackendLoginRequest = async (backendUrl, platform = 0, uid = 0, redirectUri = '', createPkceValuesFn = createPkceValues, createRandomUuid = () => globalThis.crypto.randomUUID()) => {
|
|
1539
1652
|
const effectiveRedirectUri = await getEffectiveRedirectUri(platform, uid, redirectUri);
|
|
1653
|
+
const {
|
|
1654
|
+
codeChallenge,
|
|
1655
|
+
codeVerifier
|
|
1656
|
+
} = await createPkceValuesFn();
|
|
1657
|
+
const loginUrl = new URL(getBackendAuthUrl(backendUrl, '/oidc/auth'));
|
|
1658
|
+
loginUrl.searchParams.set('client_id', oidcClientId);
|
|
1659
|
+
loginUrl.searchParams.set('code_challenge', codeChallenge);
|
|
1660
|
+
loginUrl.searchParams.set('code_challenge_method', 'S256');
|
|
1661
|
+
loginUrl.searchParams.set('nonce', createRandomUuid());
|
|
1662
|
+
loginUrl.searchParams.set('prompt', 'consent');
|
|
1663
|
+
loginUrl.searchParams.set('response_type', 'code');
|
|
1664
|
+
loginUrl.searchParams.set('scope', oidcScope);
|
|
1665
|
+
loginUrl.searchParams.set('state', createRandomUuid());
|
|
1540
1666
|
if (effectiveRedirectUri) {
|
|
1541
1667
|
loginUrl.searchParams.set('redirect_uri', effectiveRedirectUri);
|
|
1542
1668
|
}
|
|
1543
1669
|
return {
|
|
1670
|
+
codeVerifier,
|
|
1544
1671
|
loginUrl: loginUrl.toString(),
|
|
1545
1672
|
redirectUri: effectiveRedirectUri
|
|
1546
1673
|
};
|
|
@@ -1566,52 +1693,23 @@ const isLoginResponse = value => {
|
|
|
1566
1693
|
return true;
|
|
1567
1694
|
};
|
|
1568
1695
|
|
|
1569
|
-
const getBackendNativeExchangeUrl = backendUrl => {
|
|
1570
|
-
return getBackendAuthUrl(backendUrl, '/auth/native/exchange');
|
|
1571
|
-
};
|
|
1572
|
-
|
|
1573
|
-
const getExchangeErrorMessage = async response => {
|
|
1574
|
-
try {
|
|
1575
|
-
const payload = await response.json();
|
|
1576
|
-
if (payload && typeof payload === 'object' && typeof payload.error === 'string' && payload.error) {
|
|
1577
|
-
return payload.error;
|
|
1578
|
-
}
|
|
1579
|
-
} catch {
|
|
1580
|
-
// ignore
|
|
1581
|
-
}
|
|
1582
|
-
return 'Backend authentication failed.';
|
|
1583
|
-
};
|
|
1584
|
-
const exchangeElectronAuthorizationCode = async (backendUrl, code, redirectUri) => {
|
|
1585
|
-
const response = await fetch(getBackendNativeExchangeUrl(backendUrl), {
|
|
1586
|
-
body: JSON.stringify({
|
|
1587
|
-
code,
|
|
1588
|
-
redirectUri
|
|
1589
|
-
}),
|
|
1590
|
-
credentials: 'include',
|
|
1591
|
-
headers: {
|
|
1592
|
-
Accept: 'application/json',
|
|
1593
|
-
'Content-Type': 'application/json'
|
|
1594
|
-
},
|
|
1595
|
-
method: 'POST'
|
|
1596
|
-
});
|
|
1597
|
-
if (!response.ok) {
|
|
1598
|
-
throw new Error(await getExchangeErrorMessage(response));
|
|
1599
|
-
}
|
|
1600
|
-
};
|
|
1601
|
-
|
|
1602
1696
|
const hasAuthorizationCode = value => {
|
|
1603
1697
|
return typeof value === 'string' && value.length > 0;
|
|
1604
1698
|
};
|
|
1605
|
-
const
|
|
1606
|
-
|
|
1607
|
-
|
|
1699
|
+
const getElectronAuthorizationCode = async uid => {
|
|
1700
|
+
return invoke$2('OAuthServer.getCode', String(uid));
|
|
1701
|
+
};
|
|
1702
|
+
const waitForElectronBackendLogin = async (uid, codeVerifier, timeoutMs = 30_000, pollIntervalMs = 1000, getAuthorizationCode = getElectronAuthorizationCode) => {
|
|
1703
|
+
const deadline = Date.now() + timeoutMs;
|
|
1608
1704
|
while (Date.now() < deadline) {
|
|
1609
|
-
const authorizationCode = await
|
|
1705
|
+
const authorizationCode = await getAuthorizationCode(uid);
|
|
1610
1706
|
if (hasAuthorizationCode(authorizationCode)) {
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1707
|
+
return {
|
|
1708
|
+
authCode: authorizationCode,
|
|
1709
|
+
authCodeVerifier: codeVerifier,
|
|
1710
|
+
authErrorMessage: '',
|
|
1711
|
+
userState: 'loggedOut'
|
|
1712
|
+
};
|
|
1615
1713
|
}
|
|
1616
1714
|
await delay(pollIntervalMs);
|
|
1617
1715
|
}
|
|
@@ -1653,11 +1751,11 @@ const handleClickLogin = async options => {
|
|
|
1653
1751
|
}
|
|
1654
1752
|
const uid = 0;
|
|
1655
1753
|
const {
|
|
1656
|
-
|
|
1657
|
-
|
|
1754
|
+
codeVerifier,
|
|
1755
|
+
loginUrl
|
|
1658
1756
|
} = await getBackendLoginRequest(backendUrl, platform, uid);
|
|
1659
1757
|
await invoke$1('Open.openUrl', loginUrl, platform, authUseRedirect);
|
|
1660
|
-
const authState = platform === Electron ? await waitForElectronBackendLogin(
|
|
1758
|
+
const authState = platform === Electron ? await waitForElectronBackendLogin(uid, codeVerifier) : await waitForBackendLogin(backendUrl);
|
|
1661
1759
|
return {
|
|
1662
1760
|
...authState
|
|
1663
1761
|
};
|