@lvce-editor/auth-worker 1.15.0 → 1.17.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.
Files changed (2) hide show
  1. package/dist/authWorkerMain.js +101 -161
  2. package/package.json +1 -1
@@ -1184,14 +1184,21 @@ const getString = (value, fallback = '') => {
1184
1184
  return typeof value === 'string' ? value : fallback;
1185
1185
  };
1186
1186
 
1187
+ const getUserName = value => {
1188
+ if (typeof value.userName === 'string') {
1189
+ return value.userName;
1190
+ }
1191
+ return getString(value.user?.displayName);
1192
+ };
1187
1193
  const toBackendAuthState = value => {
1188
1194
  return {
1189
1195
  authAccessToken: getString(value.accessToken),
1190
1196
  authErrorMessage: getString(value.error),
1191
1197
  authRefreshToken: getString(value.refreshToken),
1192
- userName: getString(value.userName),
1198
+ userName: getUserName(value),
1193
1199
  userState: value.accessToken ? 'loggedIn' : 'loggedOut',
1194
1200
  userSubscriptionPlan: getString(value.subscriptionPlan),
1201
+ userSubscriptionStatus: getString(value.subscriptionStatus),
1195
1202
  userUsedTokens: getNumber(value.usedTokens)
1196
1203
  };
1197
1204
  };
@@ -1203,6 +1210,13 @@ const parseBackendAuthResponse = value => {
1203
1210
  return toBackendAuthState(value);
1204
1211
  };
1205
1212
 
1213
+ const getPayload = async response => {
1214
+ try {
1215
+ return await response.json();
1216
+ } catch {
1217
+ return undefined;
1218
+ }
1219
+ };
1206
1220
  const syncBackendAuth = async backendUrl => {
1207
1221
  if (!backendUrl) {
1208
1222
  return getLoggedOutBackendAuthState('Backend URL is missing.');
@@ -1222,12 +1236,7 @@ const syncBackendAuth = async backendUrl => {
1222
1236
  if (response.status === 401 || response.status === 403) {
1223
1237
  return getLoggedOutBackendAuthState();
1224
1238
  }
1225
- let payload = undefined;
1226
- try {
1227
- payload = await response.json();
1228
- } catch {
1229
- payload = undefined;
1230
- }
1239
+ const payload = await getPayload(response);
1231
1240
  if (!response.ok) {
1232
1241
  const parsed = parseBackendAuthResponse(payload);
1233
1242
  return getLoggedOutBackendAuthState(parsed.authErrorMessage || 'Backend authentication failed.');
@@ -1718,16 +1727,6 @@ async function tokenEndpointRequest(as, client, clientAuthentication, grantType,
1718
1727
  }
1719
1728
  const idTokenClaims = new WeakMap();
1720
1729
  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
1730
  async function processGenericAccessTokenResponse(as, client, response, additionalRequiredIdTokenClaims, decryptFn, recognizedTokenTypes) {
1732
1731
  assertAs(as);
1733
1732
  assertClient(client);
@@ -1773,9 +1772,6 @@ async function processGenericAccessTokenResponse(as, client, response, additiona
1773
1772
  assertNumber(client.default_max_age, true, '"client.default_max_age"');
1774
1773
  requiredClaims.push('auth_time');
1775
1774
  }
1776
- if (additionalRequiredIdTokenClaims?.length) {
1777
- requiredClaims.push(...additionalRequiredIdTokenClaims);
1778
- }
1779
1775
  const {
1780
1776
  claims,
1781
1777
  jwt
@@ -1853,27 +1849,6 @@ function validateIssuer(as, result) {
1853
1849
  return result;
1854
1850
  }
1855
1851
  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
1852
  const jwtClaimNames = {
1878
1853
  aud: 'audience',
1879
1854
  c_hash: 'code hash',
@@ -1901,98 +1876,6 @@ function validatePresence(required, result) {
1901
1876
  }
1902
1877
  return result;
1903
1878
  }
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
1879
  const WWW_AUTHENTICATE_CHALLENGE = 'OAUTH_WWW_AUTHENTICATE_CHALLENGE';
1997
1880
  const RESPONSE_BODY_ERROR = 'OAUTH_RESPONSE_BODY_ERROR';
1998
1881
  const UNSUPPORTED_OPERATION = 'OAUTH_UNSUPPORTED_OPERATION';
@@ -2006,6 +1889,15 @@ const JWT_TIMESTAMP_CHECK = 'OAUTH_JWT_TIMESTAMP_CHECK_FAILED';
2006
1889
  const JWT_CLAIM_COMPARISON = 'OAUTH_JWT_CLAIM_COMPARISON_FAILED';
2007
1890
  const MISSING_SERVER_METADATA = 'OAUTH_MISSING_SERVER_METADATA';
2008
1891
  const INVALID_SERVER_METADATA = 'OAUTH_INVALID_SERVER_METADATA';
1892
+ async function genericTokenEndpointRequest(as, client, clientAuthentication, grantType, parameters, options) {
1893
+ assertAs(as);
1894
+ assertClient(client);
1895
+ assertString(grantType, '"grantType"');
1896
+ return tokenEndpointRequest(as, client, clientAuthentication, grantType, new URLSearchParams(parameters), options);
1897
+ }
1898
+ async function processGenericTokenEndpointResponse(as, client, response, options) {
1899
+ return processGenericAccessTokenResponse(as, client, response, undefined, options?.[jweDecrypt], options?.recognizedTokenTypes);
1900
+ }
2009
1901
  function assertReadableResponse(response) {
2010
1902
  if (response.bodyUsed) {
2011
1903
  throw CodedTypeError('"response" body has been used already', ERR_INVALID_ARG_VALUE);
@@ -2155,16 +2047,6 @@ function checkSigningAlgorithm(client, issuer, fallback, header) {
2155
2047
  fallback
2156
2048
  });
2157
2049
  }
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
2050
  async function getResponseJsonBody(response, check = assertApplicationJson) {
2169
2051
  let json;
2170
2052
  try {
@@ -2507,15 +2389,76 @@ const getLoggedInState = (state, response) => {
2507
2389
  userName: typeof response.userName === 'string' ? response.userName : state.userName,
2508
2390
  userState: accessToken ? 'loggedIn' : 'loggedOut',
2509
2391
  userSubscriptionPlan: typeof response.subscriptionPlan === 'string' ? response.subscriptionPlan : state.userSubscriptionPlan,
2392
+ userSubscriptionStatus: typeof response.subscriptionStatus === 'string' ? response.subscriptionStatus : state.userSubscriptionStatus,
2510
2393
  userUsedTokens: typeof response.usedTokens === 'number' ? response.usedTokens : state.userUsedTokens
2511
2394
  };
2512
2395
  };
2513
2396
 
2514
2397
  const isLoginResponse = value => {
2515
- if (!value || typeof value !== 'object') {
2516
- return false;
2398
+ return !!value && typeof value === 'object';
2399
+ };
2400
+
2401
+ const databaseName = 'auth-worker';
2402
+ const objectStoreName = 'auth';
2403
+ const memoryStorage = new Map();
2404
+ let databasePromise;
2405
+
2406
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
2407
+ const transactionToPromise = transaction => {
2408
+ return new Promise((resolve, reject) => {
2409
+ transaction.addEventListener('complete', () => {
2410
+ resolve();
2411
+ });
2412
+ transaction.addEventListener('abort', () => {
2413
+ reject(transaction.error ?? new Error('Persistent storage transaction failed.'));
2414
+ });
2415
+ transaction.addEventListener('error', () => {
2416
+ reject(transaction.error ?? new Error('Persistent storage transaction failed.'));
2417
+ });
2418
+ });
2419
+ };
2420
+ const getDatabase = async () => {
2421
+ if (typeof indexedDB === 'undefined') {
2422
+ return undefined;
2517
2423
  }
2518
- return true;
2424
+ if (!databasePromise) {
2425
+ databasePromise = new Promise((resolve, reject) => {
2426
+ const request = indexedDB.open(databaseName, 1);
2427
+ request.addEventListener('upgradeneeded', () => {
2428
+ const database = request.result;
2429
+ if (!database.objectStoreNames.contains(objectStoreName)) {
2430
+ database.createObjectStore(objectStoreName);
2431
+ }
2432
+ });
2433
+ request.addEventListener('success', () => {
2434
+ resolve(request.result);
2435
+ });
2436
+ request.addEventListener('error', () => {
2437
+ reject(request.error ?? new Error('Failed to open persistent auth storage.'));
2438
+ });
2439
+ });
2440
+ }
2441
+ return databasePromise;
2442
+ };
2443
+ const setPersistentAuthValue = async (key, value) => {
2444
+ memoryStorage.set(key, value);
2445
+ const database = await getDatabase();
2446
+ if (!database) {
2447
+ return;
2448
+ }
2449
+ const transaction = database.transaction(objectStoreName, 'readwrite');
2450
+ const objectStore = transaction.objectStore(objectStoreName);
2451
+ objectStore.put(value, key);
2452
+ await transactionToPromise(transaction);
2453
+ };
2454
+
2455
+ const persistLoginResult = async loginResult => {
2456
+ if (loginResult.userState !== 'loggedIn') {
2457
+ return loginResult;
2458
+ }
2459
+ await setPersistentAuthValue('accessToken', loginResult.authAccessToken ?? '');
2460
+ await setPersistentAuthValue('refreshToken', loginResult.authRefreshToken ?? '');
2461
+ return loginResult;
2519
2462
  };
2520
2463
 
2521
2464
  const getBackendOidcTokenUrl = backendUrl => {
@@ -2534,15 +2477,15 @@ const getClient = () => {
2534
2477
  client_id: oidcClientId
2535
2478
  };
2536
2479
  };
2537
- const exchangeElectronAuthorizationCode = async (backendUrl, code, redirectUri, codeVerifier, nonce, requestAuthorizationCodeGrant = authorizationCodeGrantRequest, processAuthorizationCodeGrantResponse = processAuthorizationCodeResponse) => {
2480
+ const exchangeElectronAuthorizationCode = async (backendUrl, code, redirectUri, codeVerifier, requestTokenEndpoint = genericTokenEndpointRequest, processTokenEndpointResponse = processGenericTokenEndpointResponse) => {
2538
2481
  const authorizationServer = getAuthorizationServer(backendUrl);
2539
2482
  const client = getClient();
2540
- const response = await requestAuthorizationCodeGrant(authorizationServer, client, None(), new URLSearchParams({
2541
- code
2542
- }), redirectUri, codeVerifier);
2543
- const tokenResponse = await processAuthorizationCodeGrantResponse(authorizationServer, client, response, {
2544
- expectedNonce: nonce
2545
- });
2483
+ const response = await requestTokenEndpoint(authorizationServer, client, None(), 'authorization_code', new URLSearchParams({
2484
+ code,
2485
+ code_verifier: codeVerifier,
2486
+ redirect_uri: redirectUri
2487
+ }));
2488
+ const tokenResponse = await processTokenEndpointResponse(authorizationServer, client, response);
2546
2489
  return {
2547
2490
  accessToken: tokenResponse.access_token,
2548
2491
  refreshToken: typeof tokenResponse.refresh_token === 'string' ? tokenResponse.refresh_token : ''
@@ -2555,12 +2498,12 @@ const hasAuthorizationCode = value => {
2555
2498
  const getElectronAuthorizationCode = async uid => {
2556
2499
  return invoke$2('OAuthServer.getCode', String(uid));
2557
2500
  };
2558
- const waitForElectronBackendLogin = async (backendUrl, uid, redirectUri, nonce, codeVerifier, timeoutMs = 30_000, pollIntervalMs = 1000, getAuthorizationCode = getElectronAuthorizationCode, exchangeAuthorizationCode = exchangeElectronAuthorizationCode) => {
2501
+ const waitForElectronBackendLogin = async (backendUrl, uid, redirectUri, codeVerifier, timeoutMs = 30_000, pollIntervalMs = 1000, getAuthorizationCode = getElectronAuthorizationCode, exchangeAuthorizationCode = exchangeElectronAuthorizationCode) => {
2559
2502
  const deadline = Date.now() + timeoutMs;
2560
2503
  while (Date.now() < deadline) {
2561
2504
  const authorizationCode = await getAuthorizationCode(uid);
2562
2505
  if (hasAuthorizationCode(authorizationCode)) {
2563
- const tokenResponse = await exchangeAuthorizationCode(backendUrl, authorizationCode, redirectUri, codeVerifier, nonce);
2506
+ const tokenResponse = await exchangeAuthorizationCode(backendUrl, authorizationCode, redirectUri, codeVerifier);
2564
2507
  return {
2565
2508
  authAccessToken: tokenResponse.accessToken,
2566
2509
  authErrorMessage: '',
@@ -2604,20 +2547,17 @@ const handleClickLogin = async options => {
2604
2547
  userState: 'loggedOut'
2605
2548
  };
2606
2549
  }
2607
- return getLoggedInState(signingInState, response);
2550
+ return persistLoginResult(getLoggedInState(signingInState, response));
2608
2551
  }
2609
2552
  const uid = 0;
2610
2553
  const {
2611
2554
  codeVerifier,
2612
2555
  loginUrl,
2613
- nonce,
2614
2556
  redirectUri
2615
2557
  } = await getBackendLoginRequest(backendUrl, platform, uid);
2616
2558
  await invoke$1('Open.openUrl', loginUrl, platform, authUseRedirect);
2617
- const authState = platform === Electron ? await waitForElectronBackendLogin(backendUrl, uid, redirectUri, nonce, codeVerifier) : await waitForBackendLogin(backendUrl);
2618
- return {
2619
- ...authState
2620
- };
2559
+ const authState = platform === Electron ? await waitForElectronBackendLogin(backendUrl, uid, redirectUri, codeVerifier) : await waitForBackendLogin(backendUrl);
2560
+ return persistLoginResult(authState);
2621
2561
  } catch (error) {
2622
2562
  const errorMessage = error instanceof Error && error.message ? error.message : 'Backend authentication failed.';
2623
2563
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/auth-worker",
3
- "version": "1.15.0",
3
+ "version": "1.17.0",
4
4
  "description": "Auth Worker",
5
5
  "repository": {
6
6
  "type": "git",