oauth.do 0.1.13 → 0.1.15
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/cli.js +80 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +123 -4
- package/dist/index.js +203 -9
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +212 -53
- package/dist/node.js.map +1 -1
- package/package.json +7 -3
package/dist/node.js
CHANGED
|
@@ -26,9 +26,9 @@ function getEnv2(key) {
|
|
|
26
26
|
if (typeof process !== "undefined" && process.env?.[key]) return process.env[key];
|
|
27
27
|
return void 0;
|
|
28
28
|
}
|
|
29
|
-
function createSecureStorage() {
|
|
29
|
+
function createSecureStorage(storagePath) {
|
|
30
30
|
if (isNode()) {
|
|
31
|
-
return new SecureFileTokenStorage();
|
|
31
|
+
return new SecureFileTokenStorage(storagePath);
|
|
32
32
|
}
|
|
33
33
|
if (typeof localStorage !== "undefined") {
|
|
34
34
|
return new LocalStorageTokenStorage();
|
|
@@ -133,6 +133,10 @@ var init_storage = __esm({
|
|
|
133
133
|
tokenPath = null;
|
|
134
134
|
configDir = null;
|
|
135
135
|
initialized = false;
|
|
136
|
+
customPath;
|
|
137
|
+
constructor(customPath) {
|
|
138
|
+
this.customPath = customPath;
|
|
139
|
+
}
|
|
136
140
|
async init() {
|
|
137
141
|
if (this.initialized) return this.tokenPath !== null;
|
|
138
142
|
this.initialized = true;
|
|
@@ -140,8 +144,14 @@ var init_storage = __esm({
|
|
|
140
144
|
try {
|
|
141
145
|
const os = await import('os');
|
|
142
146
|
const path = await import('path');
|
|
143
|
-
|
|
144
|
-
|
|
147
|
+
if (this.customPath) {
|
|
148
|
+
const expandedPath = this.customPath.startsWith("~/") ? path.join(os.homedir(), this.customPath.slice(2)) : this.customPath;
|
|
149
|
+
this.tokenPath = expandedPath;
|
|
150
|
+
this.configDir = path.dirname(expandedPath);
|
|
151
|
+
} else {
|
|
152
|
+
this.configDir = path.join(os.homedir(), ".oauth.do");
|
|
153
|
+
this.tokenPath = path.join(this.configDir, "token");
|
|
154
|
+
}
|
|
145
155
|
return true;
|
|
146
156
|
} catch {
|
|
147
157
|
return false;
|
|
@@ -376,7 +386,8 @@ var globalConfig = {
|
|
|
376
386
|
apiUrl: getEnv("OAUTH_API_URL") || getEnv("API_URL") || "https://apis.do",
|
|
377
387
|
clientId: getEnv("OAUTH_CLIENT_ID") || "client_01JQYTRXK9ZPD8JPJTKDCRB656",
|
|
378
388
|
authKitDomain: getEnv("OAUTH_AUTHKIT_DOMAIN") || "login.oauth.do",
|
|
379
|
-
fetch: globalThis.fetch
|
|
389
|
+
fetch: globalThis.fetch,
|
|
390
|
+
storagePath: getEnv("OAUTH_STORAGE_PATH")
|
|
380
391
|
};
|
|
381
392
|
function configure(config) {
|
|
382
393
|
globalConfig = {
|
|
@@ -470,6 +481,11 @@ async function logout(token) {
|
|
|
470
481
|
console.error("Logout error:", error);
|
|
471
482
|
}
|
|
472
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
|
+
}
|
|
473
489
|
async function getToken() {
|
|
474
490
|
const adminToken = getEnv3("DO_ADMIN_TOKEN");
|
|
475
491
|
if (adminToken) return adminToken;
|
|
@@ -485,7 +501,34 @@ async function getToken() {
|
|
|
485
501
|
}
|
|
486
502
|
try {
|
|
487
503
|
const { createSecureStorage: createSecureStorage2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
488
|
-
const
|
|
504
|
+
const config = getConfig();
|
|
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
|
+
}
|
|
489
532
|
return await storage.getToken();
|
|
490
533
|
} catch {
|
|
491
534
|
return null;
|
|
@@ -512,7 +555,7 @@ async function refreshAccessToken(refreshToken) {
|
|
|
512
555
|
grant_type: "refresh_token",
|
|
513
556
|
refresh_token: refreshToken,
|
|
514
557
|
client_id: config.clientId
|
|
515
|
-
})
|
|
558
|
+
}).toString()
|
|
516
559
|
});
|
|
517
560
|
if (!response.ok) {
|
|
518
561
|
const errorText = await response.text();
|
|
@@ -556,7 +599,7 @@ async function authorizeDevice(options = {}) {
|
|
|
556
599
|
headers: {
|
|
557
600
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
558
601
|
},
|
|
559
|
-
body
|
|
602
|
+
body: body.toString()
|
|
560
603
|
});
|
|
561
604
|
if (!response.ok) {
|
|
562
605
|
const errorText = await response.text();
|
|
@@ -592,7 +635,7 @@ async function pollForTokens(deviceCode, interval = 5, expiresIn = 600) {
|
|
|
592
635
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
593
636
|
device_code: deviceCode,
|
|
594
637
|
client_id: config.clientId
|
|
595
|
-
})
|
|
638
|
+
}).toString()
|
|
596
639
|
});
|
|
597
640
|
if (response.ok) {
|
|
598
641
|
const data = await response.json();
|
|
@@ -622,64 +665,178 @@ async function pollForTokens(deviceCode, interval = 5, expiresIn = 600) {
|
|
|
622
665
|
}
|
|
623
666
|
}
|
|
624
667
|
|
|
668
|
+
// src/github-device.ts
|
|
669
|
+
async function startGitHubDeviceFlow(options) {
|
|
670
|
+
const { clientId, scope = "user:email read:user" } = options;
|
|
671
|
+
const fetchImpl = options.fetch || globalThis.fetch;
|
|
672
|
+
if (!clientId) {
|
|
673
|
+
throw new Error("GitHub client ID is required for device authorization");
|
|
674
|
+
}
|
|
675
|
+
try {
|
|
676
|
+
const url = "https://github.com/login/device/code";
|
|
677
|
+
const body = new URLSearchParams({
|
|
678
|
+
client_id: clientId,
|
|
679
|
+
scope
|
|
680
|
+
});
|
|
681
|
+
const response = await fetchImpl(url, {
|
|
682
|
+
method: "POST",
|
|
683
|
+
headers: {
|
|
684
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
685
|
+
"Accept": "application/json"
|
|
686
|
+
},
|
|
687
|
+
body
|
|
688
|
+
});
|
|
689
|
+
if (!response.ok) {
|
|
690
|
+
const errorText = await response.text();
|
|
691
|
+
throw new Error(`GitHub device authorization failed: ${response.statusText} - ${errorText}`);
|
|
692
|
+
}
|
|
693
|
+
const data = await response.json();
|
|
694
|
+
return {
|
|
695
|
+
deviceCode: data.device_code,
|
|
696
|
+
userCode: data.user_code,
|
|
697
|
+
verificationUri: data.verification_uri,
|
|
698
|
+
expiresIn: data.expires_in,
|
|
699
|
+
interval: data.interval
|
|
700
|
+
};
|
|
701
|
+
} catch (error) {
|
|
702
|
+
console.error("GitHub device authorization error:", error);
|
|
703
|
+
throw error;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
async function pollGitHubDeviceFlow(deviceCode, options) {
|
|
707
|
+
const { clientId, interval = 5, expiresIn = 900 } = options;
|
|
708
|
+
const fetchImpl = options.fetch || globalThis.fetch;
|
|
709
|
+
if (!clientId) {
|
|
710
|
+
throw new Error("GitHub client ID is required for token polling");
|
|
711
|
+
}
|
|
712
|
+
const startTime = Date.now();
|
|
713
|
+
const timeout = expiresIn * 1e3;
|
|
714
|
+
let currentInterval = interval * 1e3;
|
|
715
|
+
while (true) {
|
|
716
|
+
if (Date.now() - startTime > timeout) {
|
|
717
|
+
throw new Error("GitHub device authorization expired. Please try again.");
|
|
718
|
+
}
|
|
719
|
+
await new Promise((resolve) => setTimeout(resolve, currentInterval));
|
|
720
|
+
try {
|
|
721
|
+
const url = "https://github.com/login/oauth/access_token";
|
|
722
|
+
const body = new URLSearchParams({
|
|
723
|
+
client_id: clientId,
|
|
724
|
+
device_code: deviceCode,
|
|
725
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
726
|
+
});
|
|
727
|
+
const response = await fetchImpl(url, {
|
|
728
|
+
method: "POST",
|
|
729
|
+
headers: {
|
|
730
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
731
|
+
"Accept": "application/json"
|
|
732
|
+
},
|
|
733
|
+
body
|
|
734
|
+
});
|
|
735
|
+
const data = await response.json();
|
|
736
|
+
if ("access_token" in data) {
|
|
737
|
+
return {
|
|
738
|
+
accessToken: data.access_token,
|
|
739
|
+
tokenType: data.token_type,
|
|
740
|
+
scope: data.scope
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
const error = data.error || "unknown";
|
|
744
|
+
switch (error) {
|
|
745
|
+
case "authorization_pending":
|
|
746
|
+
continue;
|
|
747
|
+
case "slow_down":
|
|
748
|
+
currentInterval += 5e3;
|
|
749
|
+
continue;
|
|
750
|
+
case "access_denied":
|
|
751
|
+
throw new Error("Access denied by user");
|
|
752
|
+
case "expired_token":
|
|
753
|
+
throw new Error("Device code expired");
|
|
754
|
+
default:
|
|
755
|
+
throw new Error(`GitHub token polling failed: ${error}`);
|
|
756
|
+
}
|
|
757
|
+
} catch (error) {
|
|
758
|
+
if (error instanceof Error) {
|
|
759
|
+
throw error;
|
|
760
|
+
}
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
async function getGitHubUser(accessToken, options = {}) {
|
|
766
|
+
const fetchImpl = options.fetch || globalThis.fetch;
|
|
767
|
+
if (!accessToken) {
|
|
768
|
+
throw new Error("GitHub access token is required");
|
|
769
|
+
}
|
|
770
|
+
try {
|
|
771
|
+
const response = await fetchImpl("https://api.github.com/user", {
|
|
772
|
+
method: "GET",
|
|
773
|
+
headers: {
|
|
774
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
775
|
+
"Accept": "application/vnd.github+json",
|
|
776
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
if (!response.ok) {
|
|
780
|
+
const errorText = await response.text();
|
|
781
|
+
throw new Error(`GitHub user fetch failed: ${response.statusText} - ${errorText}`);
|
|
782
|
+
}
|
|
783
|
+
const data = await response.json();
|
|
784
|
+
return {
|
|
785
|
+
id: data.id,
|
|
786
|
+
login: data.login,
|
|
787
|
+
email: data.email,
|
|
788
|
+
name: data.name,
|
|
789
|
+
avatarUrl: data.avatar_url
|
|
790
|
+
};
|
|
791
|
+
} catch (error) {
|
|
792
|
+
console.error("GitHub user fetch error:", error);
|
|
793
|
+
throw error;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
625
797
|
// src/index.ts
|
|
626
798
|
init_storage();
|
|
627
799
|
|
|
628
800
|
// src/login.ts
|
|
629
801
|
init_storage();
|
|
630
|
-
var
|
|
631
|
-
function
|
|
802
|
+
var REFRESH_BUFFER_MS2 = 5 * 60 * 1e3;
|
|
803
|
+
function isTokenExpired2(expiresAt) {
|
|
632
804
|
if (!expiresAt) return false;
|
|
633
|
-
return Date.now() >= expiresAt -
|
|
805
|
+
return Date.now() >= expiresAt - REFRESH_BUFFER_MS2;
|
|
634
806
|
}
|
|
635
807
|
async function ensureLoggedIn(options = {}) {
|
|
636
|
-
const
|
|
808
|
+
const config = getConfig();
|
|
809
|
+
const { openBrowser = true, print = console.log, provider, storage = createSecureStorage(config.storagePath) } = options;
|
|
637
810
|
const tokenData = storage.getTokenData ? await storage.getTokenData() : null;
|
|
638
811
|
const existingToken = tokenData?.accessToken || await storage.getToken();
|
|
639
812
|
if (existingToken) {
|
|
640
|
-
if (tokenData &&
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
} catch (error) {
|
|
657
|
-
console.warn("Token refresh failed:", error);
|
|
813
|
+
if (tokenData?.expiresAt && !isTokenExpired2(tokenData.expiresAt)) {
|
|
814
|
+
return { token: existingToken, isNewLogin: false };
|
|
815
|
+
}
|
|
816
|
+
if (tokenData?.refreshToken) {
|
|
817
|
+
try {
|
|
818
|
+
const newTokens = await refreshAccessToken(tokenData.refreshToken);
|
|
819
|
+
const expiresAt2 = newTokens.expires_in ? Date.now() + newTokens.expires_in * 1e3 : void 0;
|
|
820
|
+
const newData2 = {
|
|
821
|
+
accessToken: newTokens.access_token,
|
|
822
|
+
refreshToken: newTokens.refresh_token || tokenData.refreshToken,
|
|
823
|
+
expiresAt: expiresAt2
|
|
824
|
+
};
|
|
825
|
+
if (storage.setTokenData) {
|
|
826
|
+
await storage.setTokenData(newData2);
|
|
827
|
+
} else {
|
|
828
|
+
await storage.setToken(newTokens.access_token);
|
|
658
829
|
}
|
|
830
|
+
return { token: newTokens.access_token, isNewLogin: false };
|
|
831
|
+
} catch (error) {
|
|
832
|
+
console.warn("Token refresh failed:", error);
|
|
659
833
|
}
|
|
660
|
-
}
|
|
834
|
+
}
|
|
835
|
+
if (!tokenData?.expiresAt && !tokenData?.refreshToken) {
|
|
661
836
|
const { user } = await getUser(existingToken);
|
|
662
837
|
if (user) {
|
|
663
838
|
return { token: existingToken, isNewLogin: false };
|
|
664
839
|
}
|
|
665
|
-
if (tokenData?.refreshToken) {
|
|
666
|
-
try {
|
|
667
|
-
const newTokens = await refreshAccessToken(tokenData.refreshToken);
|
|
668
|
-
const expiresAt2 = newTokens.expires_in ? Date.now() + newTokens.expires_in * 1e3 : void 0;
|
|
669
|
-
const newData2 = {
|
|
670
|
-
accessToken: newTokens.access_token,
|
|
671
|
-
refreshToken: newTokens.refresh_token || tokenData.refreshToken,
|
|
672
|
-
expiresAt: expiresAt2
|
|
673
|
-
};
|
|
674
|
-
if (storage.setTokenData) {
|
|
675
|
-
await storage.setTokenData(newData2);
|
|
676
|
-
} else {
|
|
677
|
-
await storage.setToken(newTokens.access_token);
|
|
678
|
-
}
|
|
679
|
-
return { token: newTokens.access_token, isNewLogin: false };
|
|
680
|
-
} catch {
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
840
|
}
|
|
684
841
|
}
|
|
685
842
|
print("\nLogging in...\n");
|
|
@@ -719,16 +876,18 @@ async function ensureLoggedIn(options = {}) {
|
|
|
719
876
|
return { token: tokenResponse.access_token, isNewLogin: true };
|
|
720
877
|
}
|
|
721
878
|
async function forceLogin(options = {}) {
|
|
722
|
-
const
|
|
879
|
+
const config = getConfig();
|
|
880
|
+
const { storage = createSecureStorage(config.storagePath) } = options;
|
|
723
881
|
await storage.removeToken();
|
|
724
882
|
return ensureLoggedIn(options);
|
|
725
883
|
}
|
|
726
884
|
async function ensureLoggedOut(options = {}) {
|
|
727
|
-
const
|
|
885
|
+
const config = getConfig();
|
|
886
|
+
const { print = console.log, storage = createSecureStorage(config.storagePath) } = options;
|
|
728
887
|
await storage.removeToken();
|
|
729
888
|
print("Logged out successfully\n");
|
|
730
889
|
}
|
|
731
890
|
|
|
732
|
-
export { CompositeTokenStorage, FileTokenStorage, KeychainTokenStorage, LocalStorageTokenStorage, MemoryTokenStorage, SecureFileTokenStorage, auth, authorizeDevice, buildAuthUrl, configure, createSecureStorage, ensureLoggedIn, ensureLoggedOut, forceLogin, getConfig, getToken, getUser, isAuthenticated, login, logout, pollForTokens };
|
|
891
|
+
export { CompositeTokenStorage, FileTokenStorage, KeychainTokenStorage, LocalStorageTokenStorage, MemoryTokenStorage, SecureFileTokenStorage, auth, authorizeDevice, buildAuthUrl, configure, createSecureStorage, ensureLoggedIn, ensureLoggedOut, forceLogin, getConfig, getGitHubUser, getToken, getUser, isAuthenticated, login, logout, pollForTokens, pollGitHubDeviceFlow, startGitHubDeviceFlow };
|
|
733
892
|
//# sourceMappingURL=node.js.map
|
|
734
893
|
//# sourceMappingURL=node.js.map
|