oauth.do 0.1.14 → 0.2.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/node.js CHANGED
@@ -8,6 +8,17 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // src/utils.ts
12
+ function getEnv(key) {
13
+ if (globalThis[key]) return globalThis[key];
14
+ if (typeof process !== "undefined" && process.env?.[key]) return process.env[key];
15
+ return void 0;
16
+ }
17
+ var init_utils = __esm({
18
+ "src/utils.ts"() {
19
+ }
20
+ });
21
+
11
22
  // src/storage.ts
12
23
  var storage_exports = {};
13
24
  __export(storage_exports, {
@@ -22,10 +33,6 @@ __export(storage_exports, {
22
33
  function isNode() {
23
34
  return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
24
35
  }
25
- function getEnv2(key) {
26
- if (typeof process !== "undefined" && process.env?.[key]) return process.env[key];
27
- return void 0;
28
- }
29
36
  function createSecureStorage(storagePath) {
30
37
  if (isNode()) {
31
38
  return new SecureFileTokenStorage(storagePath);
@@ -38,6 +45,7 @@ function createSecureStorage(storagePath) {
38
45
  var KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT, KeychainTokenStorage, SecureFileTokenStorage, FileTokenStorage, MemoryTokenStorage, LocalStorageTokenStorage, CompositeTokenStorage;
39
46
  var init_storage = __esm({
40
47
  "src/storage.ts"() {
48
+ init_utils();
41
49
  KEYCHAIN_SERVICE = "oauth.do";
42
50
  KEYCHAIN_ACCOUNT = "access_token";
43
51
  KeychainTokenStorage = class {
@@ -57,7 +65,7 @@ var init_storage = __esm({
57
65
  const keytarModule = imported.default || imported;
58
66
  this.keytar = keytarModule;
59
67
  if (typeof this.keytar.getPassword !== "function") {
60
- if (getEnv2("DEBUG")) {
68
+ if (getEnv("DEBUG")) {
61
69
  console.warn("Keytar module loaded but getPassword is not a function:", Object.keys(this.keytar));
62
70
  }
63
71
  this.keytar = null;
@@ -65,7 +73,7 @@ var init_storage = __esm({
65
73
  }
66
74
  return this.keytar;
67
75
  } catch (error) {
68
- if (getEnv2("DEBUG")) {
76
+ if (getEnv("DEBUG")) {
69
77
  console.warn("Keychain storage not available:", error);
70
78
  }
71
79
  return null;
@@ -80,7 +88,7 @@ var init_storage = __esm({
80
88
  const token = await keytar.getPassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
81
89
  return token;
82
90
  } catch (error) {
83
- if (getEnv2("DEBUG")) {
91
+ if (getEnv("DEBUG")) {
84
92
  console.warn("Failed to get token from keychain:", error);
85
93
  }
86
94
  return null;
@@ -122,7 +130,7 @@ var init_storage = __esm({
122
130
  await keytar.getPassword(KEYCHAIN_SERVICE, "__test__");
123
131
  return true;
124
132
  } catch (error) {
125
- if (getEnv2("DEBUG")) {
133
+ if (getEnv("DEBUG")) {
126
134
  console.warn("Keychain not available:", error);
127
135
  }
128
136
  return false;
@@ -167,7 +175,7 @@ var init_storage = __esm({
167
175
  const fs = await import('fs/promises');
168
176
  const stats = await fs.stat(this.tokenPath);
169
177
  const mode = stats.mode & 511;
170
- if (mode !== 384 && getEnv2("DEBUG")) {
178
+ if (mode !== 384 && getEnv("DEBUG")) {
171
179
  console.warn(
172
180
  `Warning: Token file has insecure permissions (${mode.toString(8)}). Expected 600. Run: chmod 600 ${this.tokenPath}`
173
181
  );
@@ -346,7 +354,7 @@ var init_storage = __esm({
346
354
  try {
347
355
  await this.keychainStorage.setToken(fileToken);
348
356
  await this.fileStorage.removeToken();
349
- if (getEnv2("DEBUG")) {
357
+ if (getEnv("DEBUG")) {
350
358
  console.log("Migrated token from file to keychain");
351
359
  }
352
360
  } catch {
@@ -377,11 +385,7 @@ var init_storage = __esm({
377
385
  });
378
386
 
379
387
  // src/config.ts
380
- function getEnv(key) {
381
- if (globalThis[key]) return globalThis[key];
382
- if (typeof process !== "undefined" && process.env?.[key]) return process.env[key];
383
- return void 0;
384
- }
388
+ init_utils();
385
389
  var globalConfig = {
386
390
  apiUrl: getEnv("OAUTH_API_URL") || getEnv("API_URL") || "https://apis.do",
387
391
  clientId: getEnv("OAUTH_CLIENT_ID") || "client_01JQYTRXK9ZPD8JPJTKDCRB656",
@@ -400,6 +404,7 @@ function getConfig() {
400
404
  }
401
405
 
402
406
  // src/auth.ts
407
+ init_utils();
403
408
  async function resolveSecret(value) {
404
409
  if (!value) return null;
405
410
  if (typeof value === "string") return value;
@@ -408,14 +413,9 @@ async function resolveSecret(value) {
408
413
  }
409
414
  return null;
410
415
  }
411
- function getEnv3(key) {
412
- if (globalThis[key]) return globalThis[key];
413
- if (typeof process !== "undefined" && process.env?.[key]) return process.env[key];
414
- return void 0;
415
- }
416
416
  async function getUser(token) {
417
417
  const config = getConfig();
418
- const authToken = token || getEnv3("DO_TOKEN") || "";
418
+ const authToken = token || getEnv("DO_TOKEN") || "";
419
419
  if (!authToken) {
420
420
  return { user: null };
421
421
  }
@@ -462,7 +462,7 @@ async function login(credentials) {
462
462
  }
463
463
  async function logout(token) {
464
464
  const config = getConfig();
465
- const authToken = token || getEnv3("DO_TOKEN") || "";
465
+ const authToken = token || getEnv("DO_TOKEN") || "";
466
466
  if (!authToken) {
467
467
  return;
468
468
  }
@@ -481,10 +481,15 @@ async function logout(token) {
481
481
  console.error("Logout error:", error);
482
482
  }
483
483
  }
484
+ var REFRESH_BUFFER_MS = 5 * 60 * 1e3;
485
+ function isTokenExpired(expiresAt) {
486
+ if (!expiresAt) return false;
487
+ return Date.now() >= expiresAt - REFRESH_BUFFER_MS;
488
+ }
484
489
  async function getToken() {
485
- const adminToken = getEnv3("DO_ADMIN_TOKEN");
490
+ const adminToken = getEnv("DO_ADMIN_TOKEN");
486
491
  if (adminToken) return adminToken;
487
- const doToken = getEnv3("DO_TOKEN");
492
+ const doToken = getEnv("DO_TOKEN");
488
493
  if (doToken) return doToken;
489
494
  try {
490
495
  const { env } = await import('cloudflare:workers');
@@ -498,6 +503,32 @@ async function getToken() {
498
503
  const { createSecureStorage: createSecureStorage2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
499
504
  const config = getConfig();
500
505
  const storage = createSecureStorage2(config.storagePath);
506
+ const tokenData = storage.getTokenData ? await storage.getTokenData() : null;
507
+ if (tokenData) {
508
+ if (!isTokenExpired(tokenData.expiresAt)) {
509
+ return tokenData.accessToken;
510
+ }
511
+ if (tokenData.refreshToken) {
512
+ try {
513
+ const newTokens = await refreshAccessToken(tokenData.refreshToken);
514
+ const expiresAt = newTokens.expires_in ? Date.now() + newTokens.expires_in * 1e3 : void 0;
515
+ const newData = {
516
+ accessToken: newTokens.access_token,
517
+ refreshToken: newTokens.refresh_token || tokenData.refreshToken,
518
+ expiresAt
519
+ };
520
+ if (storage.setTokenData) {
521
+ await storage.setTokenData(newData);
522
+ } else {
523
+ await storage.setToken(newTokens.access_token);
524
+ }
525
+ return newTokens.access_token;
526
+ } catch {
527
+ return null;
528
+ }
529
+ }
530
+ return null;
531
+ }
501
532
  return await storage.getToken();
502
533
  } catch {
503
534
  return null;
@@ -768,97 +799,116 @@ init_storage();
768
799
 
769
800
  // src/login.ts
770
801
  init_storage();
771
- var REFRESH_BUFFER_MS = 5 * 60 * 1e3;
772
- function isTokenExpired(expiresAt) {
802
+ var REFRESH_BUFFER_MS2 = 5 * 60 * 1e3;
803
+ var loginInProgress = null;
804
+ var refreshInProgress = null;
805
+ function isTokenExpired2(expiresAt) {
773
806
  if (!expiresAt) return false;
774
- return Date.now() >= expiresAt - REFRESH_BUFFER_MS;
807
+ return Date.now() >= expiresAt - REFRESH_BUFFER_MS2;
808
+ }
809
+ async function doRefresh(tokenData, storage) {
810
+ if (refreshInProgress) {
811
+ return refreshInProgress;
812
+ }
813
+ refreshInProgress = (async () => {
814
+ try {
815
+ const newTokens = await refreshAccessToken(tokenData.refreshToken);
816
+ const expiresAt = newTokens.expires_in ? Date.now() + newTokens.expires_in * 1e3 : void 0;
817
+ const newData = {
818
+ accessToken: newTokens.access_token,
819
+ refreshToken: newTokens.refresh_token || tokenData.refreshToken,
820
+ expiresAt
821
+ };
822
+ if (storage?.setTokenData) {
823
+ await storage.setTokenData(newData);
824
+ } else if (storage?.setToken) {
825
+ await storage.setToken(newTokens.access_token);
826
+ }
827
+ return { token: newTokens.access_token, isNewLogin: false };
828
+ } finally {
829
+ refreshInProgress = null;
830
+ }
831
+ })();
832
+ return refreshInProgress;
833
+ }
834
+ async function doDeviceLogin(options) {
835
+ if (loginInProgress) {
836
+ return loginInProgress;
837
+ }
838
+ const config = getConfig();
839
+ const {
840
+ openBrowser = true,
841
+ print = console.log,
842
+ provider,
843
+ storage = createSecureStorage(config.storagePath)
844
+ } = options;
845
+ loginInProgress = (async () => {
846
+ try {
847
+ print("\nLogging in...\n");
848
+ const authResponse = await authorizeDevice({ provider });
849
+ print(`To complete login:`);
850
+ print(` 1. Visit: ${authResponse.verification_uri}`);
851
+ print(` 2. Enter code: ${authResponse.user_code}`);
852
+ print(`
853
+ Or open: ${authResponse.verification_uri_complete}
854
+ `);
855
+ if (openBrowser) {
856
+ try {
857
+ const open = await import('open');
858
+ await open.default(authResponse.verification_uri_complete);
859
+ print("Browser opened automatically\n");
860
+ } catch {
861
+ }
862
+ }
863
+ print("Waiting for authorization...\n");
864
+ const tokenResponse = await pollForTokens(
865
+ authResponse.device_code,
866
+ authResponse.interval,
867
+ authResponse.expires_in
868
+ );
869
+ const expiresAt = tokenResponse.expires_in ? Date.now() + tokenResponse.expires_in * 1e3 : void 0;
870
+ const newData = {
871
+ accessToken: tokenResponse.access_token,
872
+ refreshToken: tokenResponse.refresh_token,
873
+ expiresAt
874
+ };
875
+ if (storage.setTokenData) {
876
+ await storage.setTokenData(newData);
877
+ } else {
878
+ await storage.setToken(tokenResponse.access_token);
879
+ }
880
+ print("Login successful!\n");
881
+ return { token: tokenResponse.access_token, isNewLogin: true };
882
+ } finally {
883
+ loginInProgress = null;
884
+ }
885
+ })();
886
+ return loginInProgress;
775
887
  }
776
888
  async function ensureLoggedIn(options = {}) {
777
889
  const config = getConfig();
778
- const { openBrowser = true, print = console.log, provider, storage = createSecureStorage(config.storagePath) } = options;
890
+ const { storage = createSecureStorage(config.storagePath) } = options;
779
891
  const tokenData = storage.getTokenData ? await storage.getTokenData() : null;
780
892
  const existingToken = tokenData?.accessToken || await storage.getToken();
781
893
  if (existingToken) {
782
- if (tokenData && isTokenExpired(tokenData.expiresAt)) {
783
- if (tokenData.refreshToken) {
784
- try {
785
- const newTokens = await refreshAccessToken(tokenData.refreshToken);
786
- const expiresAt2 = newTokens.expires_in ? Date.now() + newTokens.expires_in * 1e3 : void 0;
787
- const newData2 = {
788
- accessToken: newTokens.access_token,
789
- refreshToken: newTokens.refresh_token || tokenData.refreshToken,
790
- expiresAt: expiresAt2
791
- };
792
- if (storage.setTokenData) {
793
- await storage.setTokenData(newData2);
794
- } else {
795
- await storage.setToken(newTokens.access_token);
796
- }
797
- return { token: newTokens.access_token, isNewLogin: false };
798
- } catch (error) {
799
- console.warn("Token refresh failed:", error);
800
- }
894
+ if (tokenData?.expiresAt && !isTokenExpired2(tokenData.expiresAt)) {
895
+ return { token: existingToken, isNewLogin: false };
896
+ }
897
+ if (tokenData?.refreshToken) {
898
+ try {
899
+ return await doRefresh(tokenData, storage);
900
+ } catch (error) {
901
+ console.warn("Token refresh failed:", error);
801
902
  }
802
- } else {
903
+ }
904
+ if (!tokenData?.expiresAt && !tokenData?.refreshToken) {
803
905
  const { user } = await getUser(existingToken);
804
906
  if (user) {
805
907
  return { token: existingToken, isNewLogin: false };
806
908
  }
807
- if (tokenData?.refreshToken) {
808
- try {
809
- const newTokens = await refreshAccessToken(tokenData.refreshToken);
810
- const expiresAt2 = newTokens.expires_in ? Date.now() + newTokens.expires_in * 1e3 : void 0;
811
- const newData2 = {
812
- accessToken: newTokens.access_token,
813
- refreshToken: newTokens.refresh_token || tokenData.refreshToken,
814
- expiresAt: expiresAt2
815
- };
816
- if (storage.setTokenData) {
817
- await storage.setTokenData(newData2);
818
- } else {
819
- await storage.setToken(newTokens.access_token);
820
- }
821
- return { token: newTokens.access_token, isNewLogin: false };
822
- } catch {
823
- }
824
- }
825
909
  }
826
910
  }
827
- print("\nLogging in...\n");
828
- const authResponse = await authorizeDevice({ provider });
829
- print(`To complete login:`);
830
- print(` 1. Visit: ${authResponse.verification_uri}`);
831
- print(` 2. Enter code: ${authResponse.user_code}`);
832
- print(`
833
- Or open: ${authResponse.verification_uri_complete}
834
- `);
835
- if (openBrowser) {
836
- try {
837
- const open = await import('open');
838
- await open.default(authResponse.verification_uri_complete);
839
- print("Browser opened automatically\n");
840
- } catch {
841
- }
842
- }
843
- print("Waiting for authorization...\n");
844
- const tokenResponse = await pollForTokens(
845
- authResponse.device_code,
846
- authResponse.interval,
847
- authResponse.expires_in
848
- );
849
- const expiresAt = tokenResponse.expires_in ? Date.now() + tokenResponse.expires_in * 1e3 : void 0;
850
- const newData = {
851
- accessToken: tokenResponse.access_token,
852
- refreshToken: tokenResponse.refresh_token,
853
- expiresAt
854
- };
855
- if (storage.setTokenData) {
856
- await storage.setTokenData(newData);
857
- } else {
858
- await storage.setToken(tokenResponse.access_token);
859
- }
860
- print("Login successful!\n");
861
- return { token: tokenResponse.access_token, isNewLogin: true };
911
+ return doDeviceLogin(options);
862
912
  }
863
913
  async function forceLogin(options = {}) {
864
914
  const config = getConfig();