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/index.d.ts
CHANGED
|
@@ -20,6 +20,12 @@ interface OAuthConfig {
|
|
|
20
20
|
* Custom fetch implementation
|
|
21
21
|
*/
|
|
22
22
|
fetch?: typeof fetch;
|
|
23
|
+
/**
|
|
24
|
+
* Custom path for token storage
|
|
25
|
+
* Supports ~ for home directory (e.g., '~/.studio/tokens.json')
|
|
26
|
+
* @default '~/.oauth.do/token'
|
|
27
|
+
*/
|
|
28
|
+
storagePath?: string;
|
|
23
29
|
}
|
|
24
30
|
/**
|
|
25
31
|
* User information returned from auth endpoints
|
|
@@ -115,7 +121,7 @@ declare function logout(token?: string): Promise<void>;
|
|
|
115
121
|
* 1. globalThis.DO_ADMIN_TOKEN / DO_TOKEN (Workers legacy)
|
|
116
122
|
* 2. process.env.DO_ADMIN_TOKEN / DO_TOKEN (Node.js)
|
|
117
123
|
* 3. cloudflare:workers env import (Workers 2025+) - supports secrets store bindings
|
|
118
|
-
* 4. Stored token (keychain/secure file)
|
|
124
|
+
* 4. Stored token (keychain/secure file) - with automatic refresh if expired
|
|
119
125
|
*
|
|
120
126
|
* @see https://developers.cloudflare.com/changelog/2025-03-17-importable-env/
|
|
121
127
|
*/
|
|
@@ -163,7 +169,7 @@ declare function configure(config: OAuthConfig): void;
|
|
|
163
169
|
/**
|
|
164
170
|
* Get current configuration
|
|
165
171
|
*/
|
|
166
|
-
declare function getConfig(): Required<OAuthConfig>;
|
|
172
|
+
declare function getConfig(): Omit<Required<OAuthConfig>, 'storagePath'> & Pick<OAuthConfig, 'storagePath'>;
|
|
167
173
|
|
|
168
174
|
/**
|
|
169
175
|
* OAuth provider options for direct provider login
|
|
@@ -192,6 +198,115 @@ declare function authorizeDevice(options?: DeviceAuthOptions): Promise<DeviceAut
|
|
|
192
198
|
*/
|
|
193
199
|
declare function pollForTokens(deviceCode: string, interval?: number, expiresIn?: number): Promise<TokenResponse>;
|
|
194
200
|
|
|
201
|
+
/**
|
|
202
|
+
* GitHub Device Flow implementation
|
|
203
|
+
* Following OAuth 2.0 Device Authorization Grant (RFC 8628)
|
|
204
|
+
* https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#device-flow
|
|
205
|
+
*/
|
|
206
|
+
interface GitHubDeviceFlowOptions {
|
|
207
|
+
/** GitHub OAuth App client ID */
|
|
208
|
+
clientId: string;
|
|
209
|
+
/** OAuth scopes (default: 'user:email read:user') */
|
|
210
|
+
scope?: string;
|
|
211
|
+
/** Custom fetch implementation */
|
|
212
|
+
fetch?: typeof fetch;
|
|
213
|
+
}
|
|
214
|
+
interface GitHubDeviceAuthResponse {
|
|
215
|
+
/** Device verification code */
|
|
216
|
+
deviceCode: string;
|
|
217
|
+
/** User verification code to display */
|
|
218
|
+
userCode: string;
|
|
219
|
+
/** Verification URI for user to visit */
|
|
220
|
+
verificationUri: string;
|
|
221
|
+
/** Expiration time in seconds */
|
|
222
|
+
expiresIn: number;
|
|
223
|
+
/** Polling interval in seconds */
|
|
224
|
+
interval: number;
|
|
225
|
+
}
|
|
226
|
+
interface GitHubTokenResponse {
|
|
227
|
+
/** Access token for GitHub API */
|
|
228
|
+
accessToken: string;
|
|
229
|
+
/** Token type (typically 'bearer') */
|
|
230
|
+
tokenType: string;
|
|
231
|
+
/** Granted scopes */
|
|
232
|
+
scope: string;
|
|
233
|
+
}
|
|
234
|
+
interface GitHubUser {
|
|
235
|
+
/** Numeric GitHub user ID (critical for sqid generation) */
|
|
236
|
+
id: number;
|
|
237
|
+
/** GitHub username */
|
|
238
|
+
login: string;
|
|
239
|
+
/** User's email (may be null if not public) */
|
|
240
|
+
email: string | null;
|
|
241
|
+
/** User's display name */
|
|
242
|
+
name: string | null;
|
|
243
|
+
/** Avatar image URL */
|
|
244
|
+
avatarUrl: string;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Start GitHub Device Flow
|
|
248
|
+
*
|
|
249
|
+
* Initiates device authorization flow by requesting device and user codes.
|
|
250
|
+
*
|
|
251
|
+
* @param options - Client ID, scope, and optional custom fetch
|
|
252
|
+
* @returns Device authorization response with codes and URIs
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```ts
|
|
256
|
+
* const auth = await startGitHubDeviceFlow({
|
|
257
|
+
* clientId: 'Ov23liABCDEFGHIJKLMN',
|
|
258
|
+
* scope: 'user:email read:user'
|
|
259
|
+
* })
|
|
260
|
+
*
|
|
261
|
+
* console.log(`Visit ${auth.verificationUri} and enter code: ${auth.userCode}`)
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
declare function startGitHubDeviceFlow(options: GitHubDeviceFlowOptions): Promise<GitHubDeviceAuthResponse>;
|
|
265
|
+
/**
|
|
266
|
+
* Poll GitHub Device Flow for access token
|
|
267
|
+
*
|
|
268
|
+
* Polls GitHub's token endpoint until user completes authorization.
|
|
269
|
+
* Handles all error states including authorization_pending, slow_down, etc.
|
|
270
|
+
*
|
|
271
|
+
* @param deviceCode - Device code from startGitHubDeviceFlow
|
|
272
|
+
* @param options - Client ID and optional custom fetch
|
|
273
|
+
* @returns Token response with access token
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```ts
|
|
277
|
+
* const auth = await startGitHubDeviceFlow({ clientId: '...' })
|
|
278
|
+
* // User completes authorization...
|
|
279
|
+
* const token = await pollGitHubDeviceFlow(auth.deviceCode, {
|
|
280
|
+
* clientId: '...',
|
|
281
|
+
* interval: auth.interval,
|
|
282
|
+
* expiresIn: auth.expiresIn
|
|
283
|
+
* })
|
|
284
|
+
* console.log('Access token:', token.accessToken)
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
declare function pollGitHubDeviceFlow(deviceCode: string, options: GitHubDeviceFlowOptions & {
|
|
288
|
+
interval?: number;
|
|
289
|
+
expiresIn?: number;
|
|
290
|
+
}): Promise<GitHubTokenResponse>;
|
|
291
|
+
/**
|
|
292
|
+
* Get GitHub user information
|
|
293
|
+
*
|
|
294
|
+
* Fetches authenticated user's profile from GitHub API.
|
|
295
|
+
*
|
|
296
|
+
* @param accessToken - GitHub access token
|
|
297
|
+
* @param options - Optional custom fetch implementation
|
|
298
|
+
* @returns GitHub user profile
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* ```ts
|
|
302
|
+
* const user = await getGitHubUser(token.accessToken)
|
|
303
|
+
* console.log(`Logged in as ${user.login} (ID: ${user.id})`)
|
|
304
|
+
* ```
|
|
305
|
+
*/
|
|
306
|
+
declare function getGitHubUser(accessToken: string, options?: {
|
|
307
|
+
fetch?: typeof fetch;
|
|
308
|
+
}): Promise<GitHubUser>;
|
|
309
|
+
|
|
195
310
|
/**
|
|
196
311
|
* Keychain-based token storage using OS credential manager
|
|
197
312
|
* - macOS: Keychain
|
|
@@ -228,6 +343,8 @@ declare class SecureFileTokenStorage implements TokenStorage {
|
|
|
228
343
|
private tokenPath;
|
|
229
344
|
private configDir;
|
|
230
345
|
private initialized;
|
|
346
|
+
private customPath?;
|
|
347
|
+
constructor(customPath?: string);
|
|
231
348
|
private init;
|
|
232
349
|
getToken(): Promise<string | null>;
|
|
233
350
|
setToken(token: string): Promise<void>;
|
|
@@ -309,8 +426,10 @@ declare class CompositeTokenStorage implements TokenStorage {
|
|
|
309
426
|
*
|
|
310
427
|
* Note: We use file storage by default because keychain storage on macOS
|
|
311
428
|
* requires GUI authorization popups, which breaks automation and agent workflows.
|
|
429
|
+
*
|
|
430
|
+
* @param storagePath - Optional custom path for token storage (e.g., '~/.studio/tokens.json')
|
|
312
431
|
*/
|
|
313
|
-
declare function createSecureStorage(): TokenStorage;
|
|
432
|
+
declare function createSecureStorage(storagePath?: string): TokenStorage;
|
|
314
433
|
|
|
315
434
|
/**
|
|
316
435
|
* CLI-centric login utilities
|
|
@@ -352,4 +471,4 @@ declare function forceLogin(options?: LoginOptions): Promise<LoginResult>;
|
|
|
352
471
|
*/
|
|
353
472
|
declare function ensureLoggedOut(options?: LoginOptions): Promise<void>;
|
|
354
473
|
|
|
355
|
-
export { type AuthProvider, type AuthResult, CompositeTokenStorage, type DeviceAuthorizationResponse, FileTokenStorage, KeychainTokenStorage, LocalStorageTokenStorage, type LoginOptions, type LoginResult, MemoryTokenStorage, type OAuthConfig, type OAuthProvider, SecureFileTokenStorage, type TokenError, type TokenResponse, type TokenStorage, type User, ensureLoggedOut as a, auth, authorizeDevice, buildAuthUrl, configure, createSecureStorage, ensureLoggedIn as e, forceLogin as f, getConfig, getToken, getUser, isAuthenticated, login, logout, pollForTokens };
|
|
474
|
+
export { type AuthProvider, type AuthResult, CompositeTokenStorage, type DeviceAuthorizationResponse, FileTokenStorage, type GitHubDeviceAuthResponse, type GitHubDeviceFlowOptions, type GitHubTokenResponse, type GitHubUser, KeychainTokenStorage, LocalStorageTokenStorage, type LoginOptions, type LoginResult, MemoryTokenStorage, type OAuthConfig, type OAuthProvider, SecureFileTokenStorage, type TokenError, type TokenResponse, type TokenStorage, type User, ensureLoggedOut as a, auth, authorizeDevice, buildAuthUrl, configure, createSecureStorage, ensureLoggedIn as e, forceLogin as f, getConfig, getGitHubUser, getToken, getUser, isAuthenticated, login, logout, pollForTokens, pollGitHubDeviceFlow, startGitHubDeviceFlow };
|
package/dist/index.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;
|
|
@@ -498,6 +541,28 @@ async function isAuthenticated(token) {
|
|
|
498
541
|
function auth() {
|
|
499
542
|
return getToken;
|
|
500
543
|
}
|
|
544
|
+
async function refreshAccessToken(refreshToken) {
|
|
545
|
+
const config = getConfig();
|
|
546
|
+
if (!config.clientId) {
|
|
547
|
+
throw new Error("Client ID is required for token refresh");
|
|
548
|
+
}
|
|
549
|
+
const response = await config.fetch("https://auth.apis.do/user_management/authenticate", {
|
|
550
|
+
method: "POST",
|
|
551
|
+
headers: {
|
|
552
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
553
|
+
},
|
|
554
|
+
body: new URLSearchParams({
|
|
555
|
+
grant_type: "refresh_token",
|
|
556
|
+
refresh_token: refreshToken,
|
|
557
|
+
client_id: config.clientId
|
|
558
|
+
}).toString()
|
|
559
|
+
});
|
|
560
|
+
if (!response.ok) {
|
|
561
|
+
const errorText = await response.text();
|
|
562
|
+
throw new Error(`Token refresh failed: ${response.status} - ${errorText}`);
|
|
563
|
+
}
|
|
564
|
+
return await response.json();
|
|
565
|
+
}
|
|
501
566
|
function buildAuthUrl(options) {
|
|
502
567
|
const config = getConfig();
|
|
503
568
|
const clientId = options.clientId || config.clientId;
|
|
@@ -534,7 +599,7 @@ async function authorizeDevice(options = {}) {
|
|
|
534
599
|
headers: {
|
|
535
600
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
536
601
|
},
|
|
537
|
-
body
|
|
602
|
+
body: body.toString()
|
|
538
603
|
});
|
|
539
604
|
if (!response.ok) {
|
|
540
605
|
const errorText = await response.text();
|
|
@@ -570,7 +635,7 @@ async function pollForTokens(deviceCode, interval = 5, expiresIn = 600) {
|
|
|
570
635
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
571
636
|
device_code: deviceCode,
|
|
572
637
|
client_id: config.clientId
|
|
573
|
-
})
|
|
638
|
+
}).toString()
|
|
574
639
|
});
|
|
575
640
|
if (response.ok) {
|
|
576
641
|
const data = await response.json();
|
|
@@ -600,9 +665,138 @@ async function pollForTokens(deviceCode, interval = 5, expiresIn = 600) {
|
|
|
600
665
|
}
|
|
601
666
|
}
|
|
602
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
|
+
|
|
603
797
|
// src/index.ts
|
|
604
798
|
init_storage();
|
|
605
799
|
|
|
606
|
-
export { CompositeTokenStorage, FileTokenStorage, KeychainTokenStorage, LocalStorageTokenStorage, MemoryTokenStorage, SecureFileTokenStorage, auth, authorizeDevice, buildAuthUrl, configure, createSecureStorage, getConfig, getToken, getUser, isAuthenticated, login, logout, pollForTokens };
|
|
800
|
+
export { CompositeTokenStorage, FileTokenStorage, KeychainTokenStorage, LocalStorageTokenStorage, MemoryTokenStorage, SecureFileTokenStorage, auth, authorizeDevice, buildAuthUrl, configure, createSecureStorage, getConfig, getGitHubUser, getToken, getUser, isAuthenticated, login, logout, pollForTokens, pollGitHubDeviceFlow, startGitHubDeviceFlow };
|
|
607
801
|
//# sourceMappingURL=index.js.map
|
|
608
802
|
//# sourceMappingURL=index.js.map
|