@lvce-editor/auth-worker 1.15.0 → 1.16.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 +83 -151
- package/package.json +1 -1
package/dist/authWorkerMain.js
CHANGED
|
@@ -1718,16 +1718,6 @@ async function tokenEndpointRequest(as, client, clientAuthentication, grantType,
|
|
|
1718
1718
|
}
|
|
1719
1719
|
const idTokenClaims = new WeakMap();
|
|
1720
1720
|
const jwtRefs = new WeakMap();
|
|
1721
|
-
function getValidatedIdTokenClaims(ref) {
|
|
1722
|
-
if (!ref.id_token) {
|
|
1723
|
-
return undefined;
|
|
1724
|
-
}
|
|
1725
|
-
const claims = idTokenClaims.get(ref);
|
|
1726
|
-
if (!claims) {
|
|
1727
|
-
throw CodedTypeError('"ref" was already garbage collected or did not resolve from the proper sources', ERR_INVALID_ARG_VALUE);
|
|
1728
|
-
}
|
|
1729
|
-
return claims;
|
|
1730
|
-
}
|
|
1731
1721
|
async function processGenericAccessTokenResponse(as, client, response, additionalRequiredIdTokenClaims, decryptFn, recognizedTokenTypes) {
|
|
1732
1722
|
assertAs(as);
|
|
1733
1723
|
assertClient(client);
|
|
@@ -1773,9 +1763,6 @@ async function processGenericAccessTokenResponse(as, client, response, additiona
|
|
|
1773
1763
|
assertNumber(client.default_max_age, true, '"client.default_max_age"');
|
|
1774
1764
|
requiredClaims.push('auth_time');
|
|
1775
1765
|
}
|
|
1776
|
-
if (additionalRequiredIdTokenClaims?.length) {
|
|
1777
|
-
requiredClaims.push(...additionalRequiredIdTokenClaims);
|
|
1778
|
-
}
|
|
1779
1766
|
const {
|
|
1780
1767
|
claims,
|
|
1781
1768
|
jwt
|
|
@@ -1853,27 +1840,6 @@ function validateIssuer(as, result) {
|
|
|
1853
1840
|
return result;
|
|
1854
1841
|
}
|
|
1855
1842
|
const branded = new WeakSet();
|
|
1856
|
-
const nopkce = Symbol();
|
|
1857
|
-
async function authorizationCodeGrantRequest(as, client, clientAuthentication, callbackParameters, redirectUri, codeVerifier, options) {
|
|
1858
|
-
assertAs(as);
|
|
1859
|
-
assertClient(client);
|
|
1860
|
-
if (!branded.has(callbackParameters)) {
|
|
1861
|
-
throw CodedTypeError('"callbackParameters" must be an instance of URLSearchParams obtained from "validateAuthResponse()", or "validateJwtAuthResponse()', ERR_INVALID_ARG_VALUE);
|
|
1862
|
-
}
|
|
1863
|
-
assertString(redirectUri, '"redirectUri"');
|
|
1864
|
-
const code = getURLSearchParameter(callbackParameters, 'code');
|
|
1865
|
-
if (!code) {
|
|
1866
|
-
throw OPE('no authorization code in "callbackParameters"', INVALID_RESPONSE);
|
|
1867
|
-
}
|
|
1868
|
-
const parameters = new URLSearchParams(options?.additionalParameters);
|
|
1869
|
-
parameters.set('redirect_uri', redirectUri);
|
|
1870
|
-
parameters.set('code', code);
|
|
1871
|
-
if (codeVerifier !== nopkce) {
|
|
1872
|
-
assertString(codeVerifier, '"codeVerifier"');
|
|
1873
|
-
parameters.set('code_verifier', codeVerifier);
|
|
1874
|
-
}
|
|
1875
|
-
return tokenEndpointRequest(as, client, clientAuthentication, 'authorization_code', parameters, options);
|
|
1876
|
-
}
|
|
1877
1843
|
const jwtClaimNames = {
|
|
1878
1844
|
aud: 'audience',
|
|
1879
1845
|
c_hash: 'code hash',
|
|
@@ -1901,98 +1867,6 @@ function validatePresence(required, result) {
|
|
|
1901
1867
|
}
|
|
1902
1868
|
return result;
|
|
1903
1869
|
}
|
|
1904
|
-
const expectNoNonce = Symbol();
|
|
1905
|
-
const skipAuthTimeCheck = Symbol();
|
|
1906
|
-
async function processAuthorizationCodeResponse(as, client, response, options) {
|
|
1907
|
-
if (typeof options?.expectedNonce === 'string' || typeof options?.maxAge === 'number' || options?.requireIdToken) {
|
|
1908
|
-
return processAuthorizationCodeOpenIDResponse(as, client, response, options.expectedNonce, options.maxAge, options[jweDecrypt], options.recognizedTokenTypes);
|
|
1909
|
-
}
|
|
1910
|
-
return processAuthorizationCodeOAuth2Response(as, client, response, options?.[jweDecrypt], options?.recognizedTokenTypes);
|
|
1911
|
-
}
|
|
1912
|
-
async function processAuthorizationCodeOpenIDResponse(as, client, response, expectedNonce, maxAge, decryptFn, recognizedTokenTypes) {
|
|
1913
|
-
const additionalRequiredClaims = [];
|
|
1914
|
-
switch (expectedNonce) {
|
|
1915
|
-
case undefined:
|
|
1916
|
-
expectedNonce = expectNoNonce;
|
|
1917
|
-
break;
|
|
1918
|
-
case expectNoNonce:
|
|
1919
|
-
break;
|
|
1920
|
-
default:
|
|
1921
|
-
assertString(expectedNonce, '"expectedNonce" argument');
|
|
1922
|
-
additionalRequiredClaims.push('nonce');
|
|
1923
|
-
}
|
|
1924
|
-
maxAge ??= client.default_max_age;
|
|
1925
|
-
switch (maxAge) {
|
|
1926
|
-
case undefined:
|
|
1927
|
-
maxAge = skipAuthTimeCheck;
|
|
1928
|
-
break;
|
|
1929
|
-
case skipAuthTimeCheck:
|
|
1930
|
-
break;
|
|
1931
|
-
default:
|
|
1932
|
-
assertNumber(maxAge, true, '"maxAge" argument');
|
|
1933
|
-
additionalRequiredClaims.push('auth_time');
|
|
1934
|
-
}
|
|
1935
|
-
const result = await processGenericAccessTokenResponse(as, client, response, additionalRequiredClaims, decryptFn, recognizedTokenTypes);
|
|
1936
|
-
assertString(result.id_token, '"response" body "id_token" property', INVALID_RESPONSE, {
|
|
1937
|
-
body: result
|
|
1938
|
-
});
|
|
1939
|
-
const claims = getValidatedIdTokenClaims(result);
|
|
1940
|
-
if (maxAge !== skipAuthTimeCheck) {
|
|
1941
|
-
const now = epochTime() + getClockSkew(client);
|
|
1942
|
-
const tolerance = getClockTolerance(client);
|
|
1943
|
-
if (claims.auth_time + maxAge < now - tolerance) {
|
|
1944
|
-
throw OPE('too much time has elapsed since the last End-User authentication', JWT_TIMESTAMP_CHECK, {
|
|
1945
|
-
claims,
|
|
1946
|
-
now,
|
|
1947
|
-
tolerance,
|
|
1948
|
-
claim: 'auth_time'
|
|
1949
|
-
});
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
|
-
if (expectedNonce === expectNoNonce) {
|
|
1953
|
-
if (claims.nonce !== undefined) {
|
|
1954
|
-
throw OPE('unexpected ID Token "nonce" claim value', JWT_CLAIM_COMPARISON, {
|
|
1955
|
-
expected: undefined,
|
|
1956
|
-
claims,
|
|
1957
|
-
claim: 'nonce'
|
|
1958
|
-
});
|
|
1959
|
-
}
|
|
1960
|
-
} else if (claims.nonce !== expectedNonce) {
|
|
1961
|
-
throw OPE('unexpected ID Token "nonce" claim value', JWT_CLAIM_COMPARISON, {
|
|
1962
|
-
expected: expectedNonce,
|
|
1963
|
-
claims,
|
|
1964
|
-
claim: 'nonce'
|
|
1965
|
-
});
|
|
1966
|
-
}
|
|
1967
|
-
return result;
|
|
1968
|
-
}
|
|
1969
|
-
async function processAuthorizationCodeOAuth2Response(as, client, response, decryptFn, recognizedTokenTypes) {
|
|
1970
|
-
const result = await processGenericAccessTokenResponse(as, client, response, undefined, decryptFn, recognizedTokenTypes);
|
|
1971
|
-
const claims = getValidatedIdTokenClaims(result);
|
|
1972
|
-
if (claims) {
|
|
1973
|
-
if (client.default_max_age !== undefined) {
|
|
1974
|
-
assertNumber(client.default_max_age, true, '"client.default_max_age"');
|
|
1975
|
-
const now = epochTime() + getClockSkew(client);
|
|
1976
|
-
const tolerance = getClockTolerance(client);
|
|
1977
|
-
if (claims.auth_time + client.default_max_age < now - tolerance) {
|
|
1978
|
-
throw OPE('too much time has elapsed since the last End-User authentication', JWT_TIMESTAMP_CHECK, {
|
|
1979
|
-
claims,
|
|
1980
|
-
now,
|
|
1981
|
-
tolerance,
|
|
1982
|
-
claim: 'auth_time'
|
|
1983
|
-
});
|
|
1984
|
-
}
|
|
1985
|
-
}
|
|
1986
|
-
if (claims.nonce !== undefined) {
|
|
1987
|
-
throw OPE('unexpected ID Token "nonce" claim value', JWT_CLAIM_COMPARISON, {
|
|
1988
|
-
expected: undefined,
|
|
1989
|
-
claims,
|
|
1990
|
-
claim: 'nonce'
|
|
1991
|
-
});
|
|
1992
|
-
}
|
|
1993
|
-
}
|
|
1994
|
-
return result;
|
|
1995
|
-
}
|
|
1996
1870
|
const WWW_AUTHENTICATE_CHALLENGE = 'OAUTH_WWW_AUTHENTICATE_CHALLENGE';
|
|
1997
1871
|
const RESPONSE_BODY_ERROR = 'OAUTH_RESPONSE_BODY_ERROR';
|
|
1998
1872
|
const UNSUPPORTED_OPERATION = 'OAUTH_UNSUPPORTED_OPERATION';
|
|
@@ -2006,6 +1880,15 @@ const JWT_TIMESTAMP_CHECK = 'OAUTH_JWT_TIMESTAMP_CHECK_FAILED';
|
|
|
2006
1880
|
const JWT_CLAIM_COMPARISON = 'OAUTH_JWT_CLAIM_COMPARISON_FAILED';
|
|
2007
1881
|
const MISSING_SERVER_METADATA = 'OAUTH_MISSING_SERVER_METADATA';
|
|
2008
1882
|
const INVALID_SERVER_METADATA = 'OAUTH_INVALID_SERVER_METADATA';
|
|
1883
|
+
async function genericTokenEndpointRequest(as, client, clientAuthentication, grantType, parameters, options) {
|
|
1884
|
+
assertAs(as);
|
|
1885
|
+
assertClient(client);
|
|
1886
|
+
assertString(grantType, '"grantType"');
|
|
1887
|
+
return tokenEndpointRequest(as, client, clientAuthentication, grantType, new URLSearchParams(parameters), options);
|
|
1888
|
+
}
|
|
1889
|
+
async function processGenericTokenEndpointResponse(as, client, response, options) {
|
|
1890
|
+
return processGenericAccessTokenResponse(as, client, response, undefined, options?.[jweDecrypt], options?.recognizedTokenTypes);
|
|
1891
|
+
}
|
|
2009
1892
|
function assertReadableResponse(response) {
|
|
2010
1893
|
if (response.bodyUsed) {
|
|
2011
1894
|
throw CodedTypeError('"response" body has been used already', ERR_INVALID_ARG_VALUE);
|
|
@@ -2155,16 +2038,6 @@ function checkSigningAlgorithm(client, issuer, fallback, header) {
|
|
|
2155
2038
|
fallback
|
|
2156
2039
|
});
|
|
2157
2040
|
}
|
|
2158
|
-
function getURLSearchParameter(parameters, name) {
|
|
2159
|
-
const {
|
|
2160
|
-
0: value,
|
|
2161
|
-
length
|
|
2162
|
-
} = parameters.getAll(name);
|
|
2163
|
-
if (length > 1) {
|
|
2164
|
-
throw OPE(`"${name}" parameter must be provided only once`, INVALID_RESPONSE);
|
|
2165
|
-
}
|
|
2166
|
-
return value;
|
|
2167
|
-
}
|
|
2168
2041
|
async function getResponseJsonBody(response, check = assertApplicationJson) {
|
|
2169
2042
|
let json;
|
|
2170
2043
|
try {
|
|
@@ -2518,6 +2391,60 @@ const isLoginResponse = value => {
|
|
|
2518
2391
|
return true;
|
|
2519
2392
|
};
|
|
2520
2393
|
|
|
2394
|
+
const databaseName = 'auth-worker';
|
|
2395
|
+
const objectStoreName = 'auth';
|
|
2396
|
+
const memoryStorage = new Map();
|
|
2397
|
+
let databasePromise;
|
|
2398
|
+
|
|
2399
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
2400
|
+
const transactionToPromise = transaction => {
|
|
2401
|
+
return new Promise((resolve, reject) => {
|
|
2402
|
+
transaction.addEventListener('complete', () => {
|
|
2403
|
+
resolve();
|
|
2404
|
+
});
|
|
2405
|
+
transaction.addEventListener('abort', () => {
|
|
2406
|
+
reject(transaction.error ?? new Error('Persistent storage transaction failed.'));
|
|
2407
|
+
});
|
|
2408
|
+
transaction.addEventListener('error', () => {
|
|
2409
|
+
reject(transaction.error ?? new Error('Persistent storage transaction failed.'));
|
|
2410
|
+
});
|
|
2411
|
+
});
|
|
2412
|
+
};
|
|
2413
|
+
const getDatabase = async () => {
|
|
2414
|
+
if (typeof indexedDB === 'undefined') {
|
|
2415
|
+
return undefined;
|
|
2416
|
+
}
|
|
2417
|
+
if (!databasePromise) {
|
|
2418
|
+
databasePromise = new Promise((resolve, reject) => {
|
|
2419
|
+
const request = indexedDB.open(databaseName, 1);
|
|
2420
|
+
request.addEventListener('upgradeneeded', () => {
|
|
2421
|
+
const database = request.result;
|
|
2422
|
+
if (!database.objectStoreNames.contains(objectStoreName)) {
|
|
2423
|
+
database.createObjectStore(objectStoreName);
|
|
2424
|
+
}
|
|
2425
|
+
});
|
|
2426
|
+
request.addEventListener('success', () => {
|
|
2427
|
+
resolve(request.result);
|
|
2428
|
+
});
|
|
2429
|
+
request.addEventListener('error', () => {
|
|
2430
|
+
reject(request.error ?? new Error('Failed to open persistent auth storage.'));
|
|
2431
|
+
});
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
return databasePromise;
|
|
2435
|
+
};
|
|
2436
|
+
const setPersistentAuthValue = async (key, value) => {
|
|
2437
|
+
memoryStorage.set(key, value);
|
|
2438
|
+
const database = await getDatabase();
|
|
2439
|
+
if (!database) {
|
|
2440
|
+
return;
|
|
2441
|
+
}
|
|
2442
|
+
const transaction = database.transaction(objectStoreName, 'readwrite');
|
|
2443
|
+
const objectStore = transaction.objectStore(objectStoreName);
|
|
2444
|
+
objectStore.put(value, key);
|
|
2445
|
+
await transactionToPromise(transaction);
|
|
2446
|
+
};
|
|
2447
|
+
|
|
2521
2448
|
const getBackendOidcTokenUrl = backendUrl => {
|
|
2522
2449
|
return getBackendAuthUrl(backendUrl, '/oidc/token');
|
|
2523
2450
|
};
|
|
@@ -2534,15 +2461,15 @@ const getClient = () => {
|
|
|
2534
2461
|
client_id: oidcClientId
|
|
2535
2462
|
};
|
|
2536
2463
|
};
|
|
2537
|
-
const exchangeElectronAuthorizationCode = async (backendUrl, code, redirectUri, codeVerifier,
|
|
2464
|
+
const exchangeElectronAuthorizationCode = async (backendUrl, code, redirectUri, codeVerifier, requestTokenEndpoint = genericTokenEndpointRequest, processTokenEndpointResponse = processGenericTokenEndpointResponse) => {
|
|
2538
2465
|
const authorizationServer = getAuthorizationServer(backendUrl);
|
|
2539
2466
|
const client = getClient();
|
|
2540
|
-
const response = await
|
|
2541
|
-
code
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2467
|
+
const response = await requestTokenEndpoint(authorizationServer, client, None(), 'authorization_code', new URLSearchParams({
|
|
2468
|
+
code,
|
|
2469
|
+
code_verifier: codeVerifier,
|
|
2470
|
+
redirect_uri: redirectUri
|
|
2471
|
+
}));
|
|
2472
|
+
const tokenResponse = await processTokenEndpointResponse(authorizationServer, client, response);
|
|
2546
2473
|
return {
|
|
2547
2474
|
accessToken: tokenResponse.access_token,
|
|
2548
2475
|
refreshToken: typeof tokenResponse.refresh_token === 'string' ? tokenResponse.refresh_token : ''
|
|
@@ -2555,12 +2482,12 @@ const hasAuthorizationCode = value => {
|
|
|
2555
2482
|
const getElectronAuthorizationCode = async uid => {
|
|
2556
2483
|
return invoke$2('OAuthServer.getCode', String(uid));
|
|
2557
2484
|
};
|
|
2558
|
-
const waitForElectronBackendLogin = async (backendUrl, uid, redirectUri,
|
|
2485
|
+
const waitForElectronBackendLogin = async (backendUrl, uid, redirectUri, codeVerifier, timeoutMs = 30_000, pollIntervalMs = 1000, getAuthorizationCode = getElectronAuthorizationCode, exchangeAuthorizationCode = exchangeElectronAuthorizationCode) => {
|
|
2559
2486
|
const deadline = Date.now() + timeoutMs;
|
|
2560
2487
|
while (Date.now() < deadline) {
|
|
2561
2488
|
const authorizationCode = await getAuthorizationCode(uid);
|
|
2562
2489
|
if (hasAuthorizationCode(authorizationCode)) {
|
|
2563
|
-
const tokenResponse = await exchangeAuthorizationCode(backendUrl, authorizationCode, redirectUri, codeVerifier
|
|
2490
|
+
const tokenResponse = await exchangeAuthorizationCode(backendUrl, authorizationCode, redirectUri, codeVerifier);
|
|
2564
2491
|
return {
|
|
2565
2492
|
authAccessToken: tokenResponse.accessToken,
|
|
2566
2493
|
authErrorMessage: '',
|
|
@@ -2573,6 +2500,14 @@ const waitForElectronBackendLogin = async (backendUrl, uid, redirectUri, nonce,
|
|
|
2573
2500
|
return getLoggedOutBackendAuthState('Timed out waiting for backend login.');
|
|
2574
2501
|
};
|
|
2575
2502
|
|
|
2503
|
+
const persistLoginResult = async loginResult => {
|
|
2504
|
+
if (loginResult.userState !== 'loggedIn') {
|
|
2505
|
+
return loginResult;
|
|
2506
|
+
}
|
|
2507
|
+
await setPersistentAuthValue('accessToken', loginResult.authAccessToken ?? '');
|
|
2508
|
+
await setPersistentAuthValue('refreshToken', loginResult.authRefreshToken ?? '');
|
|
2509
|
+
return loginResult;
|
|
2510
|
+
};
|
|
2576
2511
|
const handleClickLogin = async options => {
|
|
2577
2512
|
const {
|
|
2578
2513
|
authUseRedirect,
|
|
@@ -2604,20 +2539,17 @@ const handleClickLogin = async options => {
|
|
|
2604
2539
|
userState: 'loggedOut'
|
|
2605
2540
|
};
|
|
2606
2541
|
}
|
|
2607
|
-
return getLoggedInState(signingInState, response);
|
|
2542
|
+
return persistLoginResult(getLoggedInState(signingInState, response));
|
|
2608
2543
|
}
|
|
2609
2544
|
const uid = 0;
|
|
2610
2545
|
const {
|
|
2611
2546
|
codeVerifier,
|
|
2612
2547
|
loginUrl,
|
|
2613
|
-
nonce,
|
|
2614
2548
|
redirectUri
|
|
2615
2549
|
} = await getBackendLoginRequest(backendUrl, platform, uid);
|
|
2616
2550
|
await invoke$1('Open.openUrl', loginUrl, platform, authUseRedirect);
|
|
2617
|
-
const authState = platform === Electron ? await waitForElectronBackendLogin(backendUrl, uid, redirectUri,
|
|
2618
|
-
return
|
|
2619
|
-
...authState
|
|
2620
|
-
};
|
|
2551
|
+
const authState = platform === Electron ? await waitForElectronBackendLogin(backendUrl, uid, redirectUri, codeVerifier) : await waitForBackendLogin(backendUrl);
|
|
2552
|
+
return persistLoginResult(authState);
|
|
2621
2553
|
} catch (error) {
|
|
2622
2554
|
const errorMessage = error instanceof Error && error.message ? error.message : 'Backend authentication failed.';
|
|
2623
2555
|
return {
|