oauth.do 0.1.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/README.md +126 -0
- package/dist/cli.js +800 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +305 -0
- package/dist/index.js +637 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
- package/src/mdx/hooks.mdx +119 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/storage.ts
|
|
12
|
+
var storage_exports = {};
|
|
13
|
+
__export(storage_exports, {
|
|
14
|
+
CompositeTokenStorage: () => CompositeTokenStorage,
|
|
15
|
+
FileTokenStorage: () => FileTokenStorage,
|
|
16
|
+
KeychainTokenStorage: () => KeychainTokenStorage,
|
|
17
|
+
LocalStorageTokenStorage: () => LocalStorageTokenStorage,
|
|
18
|
+
MemoryTokenStorage: () => MemoryTokenStorage,
|
|
19
|
+
SecureFileTokenStorage: () => SecureFileTokenStorage,
|
|
20
|
+
createSecureStorage: () => createSecureStorage
|
|
21
|
+
});
|
|
22
|
+
function isNode() {
|
|
23
|
+
return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
24
|
+
}
|
|
25
|
+
function getEnv2(key) {
|
|
26
|
+
if (typeof process !== "undefined" && process.env?.[key]) return process.env[key];
|
|
27
|
+
return void 0;
|
|
28
|
+
}
|
|
29
|
+
function createSecureStorage() {
|
|
30
|
+
if (isNode()) {
|
|
31
|
+
return new CompositeTokenStorage();
|
|
32
|
+
}
|
|
33
|
+
if (typeof localStorage !== "undefined") {
|
|
34
|
+
return new LocalStorageTokenStorage();
|
|
35
|
+
}
|
|
36
|
+
return new MemoryTokenStorage();
|
|
37
|
+
}
|
|
38
|
+
var KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT, KeychainTokenStorage, SecureFileTokenStorage, FileTokenStorage, MemoryTokenStorage, LocalStorageTokenStorage, CompositeTokenStorage;
|
|
39
|
+
var init_storage = __esm({
|
|
40
|
+
"src/storage.ts"() {
|
|
41
|
+
KEYCHAIN_SERVICE = "oauth.do";
|
|
42
|
+
KEYCHAIN_ACCOUNT = "access_token";
|
|
43
|
+
KeychainTokenStorage = class {
|
|
44
|
+
keytar = null;
|
|
45
|
+
initialized = false;
|
|
46
|
+
/**
|
|
47
|
+
* Lazily load keytar module
|
|
48
|
+
* Returns null if keytar is not available (e.g., missing native dependencies)
|
|
49
|
+
*/
|
|
50
|
+
async getKeytar() {
|
|
51
|
+
if (this.initialized) {
|
|
52
|
+
return this.keytar;
|
|
53
|
+
}
|
|
54
|
+
this.initialized = true;
|
|
55
|
+
try {
|
|
56
|
+
this.keytar = await import('keytar');
|
|
57
|
+
return this.keytar;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (getEnv2("DEBUG")) {
|
|
60
|
+
console.warn("Keychain storage not available:", error);
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async getToken() {
|
|
66
|
+
const keytar = await this.getKeytar();
|
|
67
|
+
if (!keytar) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const token = await keytar.getPassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
|
|
72
|
+
return token;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (getEnv2("DEBUG")) {
|
|
75
|
+
console.warn("Failed to get token from keychain:", error);
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async setToken(token) {
|
|
81
|
+
const keytar = await this.getKeytar();
|
|
82
|
+
if (!keytar) {
|
|
83
|
+
throw new Error("Keychain storage not available");
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
await keytar.setPassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT, token);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw new Error(`Failed to save token to keychain: ${error}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async removeToken() {
|
|
92
|
+
const keytar = await this.getKeytar();
|
|
93
|
+
if (!keytar) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
await keytar.deletePassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Check if keychain storage is available on this system
|
|
103
|
+
*/
|
|
104
|
+
async isAvailable() {
|
|
105
|
+
const keytar = await this.getKeytar();
|
|
106
|
+
if (!keytar) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
await keytar.getPassword(KEYCHAIN_SERVICE, "__test__");
|
|
111
|
+
return true;
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
SecureFileTokenStorage = class {
|
|
118
|
+
tokenPath = null;
|
|
119
|
+
configDir = null;
|
|
120
|
+
initialized = false;
|
|
121
|
+
async init() {
|
|
122
|
+
if (this.initialized) return this.tokenPath !== null;
|
|
123
|
+
this.initialized = true;
|
|
124
|
+
if (!isNode()) return false;
|
|
125
|
+
try {
|
|
126
|
+
const os = await import('os');
|
|
127
|
+
const path = await import('path');
|
|
128
|
+
this.configDir = path.join(os.homedir(), ".oauth.do");
|
|
129
|
+
this.tokenPath = path.join(this.configDir, "token");
|
|
130
|
+
return true;
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async getToken() {
|
|
136
|
+
if (!await this.init() || !this.tokenPath) return null;
|
|
137
|
+
try {
|
|
138
|
+
const fs = await import('fs/promises');
|
|
139
|
+
const stats = await fs.stat(this.tokenPath);
|
|
140
|
+
const mode = stats.mode & 511;
|
|
141
|
+
if (mode !== 384 && getEnv2("DEBUG")) {
|
|
142
|
+
console.warn(
|
|
143
|
+
`Warning: Token file has insecure permissions (${mode.toString(8)}). Expected 600. Run: chmod 600 ${this.tokenPath}`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
const token = await fs.readFile(this.tokenPath, "utf-8");
|
|
147
|
+
return token.trim();
|
|
148
|
+
} catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async setToken(token) {
|
|
153
|
+
if (!await this.init() || !this.tokenPath || !this.configDir) {
|
|
154
|
+
throw new Error("File storage not available");
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
const fs = await import('fs/promises');
|
|
158
|
+
await fs.mkdir(this.configDir, { recursive: true, mode: 448 });
|
|
159
|
+
await fs.writeFile(this.tokenPath, token, { encoding: "utf-8", mode: 384 });
|
|
160
|
+
await fs.chmod(this.tokenPath, 384);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.error("Failed to save token:", error);
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async removeToken() {
|
|
167
|
+
if (!await this.init() || !this.tokenPath) return;
|
|
168
|
+
try {
|
|
169
|
+
const fs = await import('fs/promises');
|
|
170
|
+
await fs.unlink(this.tokenPath);
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
FileTokenStorage = class {
|
|
176
|
+
tokenPath = null;
|
|
177
|
+
configDir = null;
|
|
178
|
+
initialized = false;
|
|
179
|
+
async init() {
|
|
180
|
+
if (this.initialized) return this.tokenPath !== null;
|
|
181
|
+
this.initialized = true;
|
|
182
|
+
if (!isNode()) return false;
|
|
183
|
+
try {
|
|
184
|
+
const os = await import('os');
|
|
185
|
+
const path = await import('path');
|
|
186
|
+
this.configDir = path.join(os.homedir(), ".oauth.do");
|
|
187
|
+
this.tokenPath = path.join(this.configDir, "token");
|
|
188
|
+
return true;
|
|
189
|
+
} catch {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async getToken() {
|
|
194
|
+
if (!await this.init() || !this.tokenPath) return null;
|
|
195
|
+
try {
|
|
196
|
+
const fs = await import('fs/promises');
|
|
197
|
+
const token = await fs.readFile(this.tokenPath, "utf-8");
|
|
198
|
+
return token.trim();
|
|
199
|
+
} catch {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async setToken(token) {
|
|
204
|
+
if (!await this.init() || !this.tokenPath || !this.configDir) {
|
|
205
|
+
throw new Error("File storage not available");
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
const fs = await import('fs/promises');
|
|
209
|
+
await fs.mkdir(this.configDir, { recursive: true });
|
|
210
|
+
await fs.writeFile(this.tokenPath, token, "utf-8");
|
|
211
|
+
} catch (error) {
|
|
212
|
+
console.error("Failed to save token:", error);
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async removeToken() {
|
|
217
|
+
if (!await this.init() || !this.tokenPath) return;
|
|
218
|
+
try {
|
|
219
|
+
const fs = await import('fs/promises');
|
|
220
|
+
await fs.unlink(this.tokenPath);
|
|
221
|
+
} catch {
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
MemoryTokenStorage = class {
|
|
226
|
+
token = null;
|
|
227
|
+
async getToken() {
|
|
228
|
+
return this.token;
|
|
229
|
+
}
|
|
230
|
+
async setToken(token) {
|
|
231
|
+
this.token = token;
|
|
232
|
+
}
|
|
233
|
+
async removeToken() {
|
|
234
|
+
this.token = null;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
LocalStorageTokenStorage = class {
|
|
238
|
+
key = "oauth.do:token";
|
|
239
|
+
async getToken() {
|
|
240
|
+
if (typeof localStorage === "undefined") {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
return localStorage.getItem(this.key);
|
|
244
|
+
}
|
|
245
|
+
async setToken(token) {
|
|
246
|
+
if (typeof localStorage === "undefined") {
|
|
247
|
+
throw new Error("localStorage is not available");
|
|
248
|
+
}
|
|
249
|
+
localStorage.setItem(this.key, token);
|
|
250
|
+
}
|
|
251
|
+
async removeToken() {
|
|
252
|
+
if (typeof localStorage === "undefined") {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
localStorage.removeItem(this.key);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
CompositeTokenStorage = class {
|
|
259
|
+
keychainStorage;
|
|
260
|
+
fileStorage;
|
|
261
|
+
preferredStorage = null;
|
|
262
|
+
constructor() {
|
|
263
|
+
this.keychainStorage = new KeychainTokenStorage();
|
|
264
|
+
this.fileStorage = new SecureFileTokenStorage();
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Determine the best available storage backend
|
|
268
|
+
*/
|
|
269
|
+
async getPreferredStorage() {
|
|
270
|
+
if (this.preferredStorage) {
|
|
271
|
+
return this.preferredStorage;
|
|
272
|
+
}
|
|
273
|
+
if (await this.keychainStorage.isAvailable()) {
|
|
274
|
+
this.preferredStorage = this.keychainStorage;
|
|
275
|
+
return this.preferredStorage;
|
|
276
|
+
}
|
|
277
|
+
this.preferredStorage = this.fileStorage;
|
|
278
|
+
return this.preferredStorage;
|
|
279
|
+
}
|
|
280
|
+
async getToken() {
|
|
281
|
+
const keychainToken = await this.keychainStorage.getToken();
|
|
282
|
+
if (keychainToken) {
|
|
283
|
+
return keychainToken;
|
|
284
|
+
}
|
|
285
|
+
const fileToken = await this.fileStorage.getToken();
|
|
286
|
+
if (fileToken) {
|
|
287
|
+
if (await this.keychainStorage.isAvailable()) {
|
|
288
|
+
try {
|
|
289
|
+
await this.keychainStorage.setToken(fileToken);
|
|
290
|
+
await this.fileStorage.removeToken();
|
|
291
|
+
if (getEnv2("DEBUG")) {
|
|
292
|
+
console.log("Migrated token from file to keychain");
|
|
293
|
+
}
|
|
294
|
+
} catch {
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return fileToken;
|
|
298
|
+
}
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
async setToken(token) {
|
|
302
|
+
const storage = await this.getPreferredStorage();
|
|
303
|
+
await storage.setToken(token);
|
|
304
|
+
}
|
|
305
|
+
async removeToken() {
|
|
306
|
+
await Promise.all([this.keychainStorage.removeToken(), this.fileStorage.removeToken()]);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Get information about the current storage backend
|
|
310
|
+
*/
|
|
311
|
+
async getStorageInfo() {
|
|
312
|
+
if (await this.keychainStorage.isAvailable()) {
|
|
313
|
+
return { type: "keychain", secure: true };
|
|
314
|
+
}
|
|
315
|
+
return { type: "file", secure: true };
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// src/config.ts
|
|
322
|
+
function getEnv(key) {
|
|
323
|
+
if (globalThis[key]) return globalThis[key];
|
|
324
|
+
if (typeof process !== "undefined" && process.env?.[key]) return process.env[key];
|
|
325
|
+
return void 0;
|
|
326
|
+
}
|
|
327
|
+
var globalConfig = {
|
|
328
|
+
apiUrl: getEnv("OAUTH_API_URL") || getEnv("API_URL") || "https://apis.do",
|
|
329
|
+
clientId: getEnv("OAUTH_CLIENT_ID") || "oauth.do",
|
|
330
|
+
authKitDomain: getEnv("OAUTH_AUTHKIT_DOMAIN") || "login.oauth.do",
|
|
331
|
+
fetch: globalThis.fetch
|
|
332
|
+
};
|
|
333
|
+
function configure(config) {
|
|
334
|
+
globalConfig = {
|
|
335
|
+
...globalConfig,
|
|
336
|
+
...config
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
function getConfig() {
|
|
340
|
+
return globalConfig;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/auth.ts
|
|
344
|
+
function getEnv3(key) {
|
|
345
|
+
if (globalThis[key]) return globalThis[key];
|
|
346
|
+
if (typeof process !== "undefined" && process.env?.[key]) return process.env[key];
|
|
347
|
+
return void 0;
|
|
348
|
+
}
|
|
349
|
+
async function auth(token) {
|
|
350
|
+
const config = getConfig();
|
|
351
|
+
const authToken = token || getEnv3("DO_TOKEN") || "";
|
|
352
|
+
if (!authToken) {
|
|
353
|
+
return { user: null };
|
|
354
|
+
}
|
|
355
|
+
try {
|
|
356
|
+
const response = await config.fetch(`${config.apiUrl}/me`, {
|
|
357
|
+
method: "GET",
|
|
358
|
+
headers: {
|
|
359
|
+
"Authorization": `Bearer ${authToken}`,
|
|
360
|
+
"Content-Type": "application/json"
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
if (!response.ok) {
|
|
364
|
+
if (response.status === 401) {
|
|
365
|
+
return { user: null };
|
|
366
|
+
}
|
|
367
|
+
throw new Error(`Authentication failed: ${response.statusText}`);
|
|
368
|
+
}
|
|
369
|
+
const user = await response.json();
|
|
370
|
+
return { user, token: authToken };
|
|
371
|
+
} catch (error) {
|
|
372
|
+
console.error("Auth error:", error);
|
|
373
|
+
return { user: null };
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async function login(credentials) {
|
|
377
|
+
const config = getConfig();
|
|
378
|
+
try {
|
|
379
|
+
const response = await config.fetch(`${config.apiUrl}/login`, {
|
|
380
|
+
method: "POST",
|
|
381
|
+
headers: {
|
|
382
|
+
"Content-Type": "application/json"
|
|
383
|
+
},
|
|
384
|
+
body: JSON.stringify(credentials)
|
|
385
|
+
});
|
|
386
|
+
if (!response.ok) {
|
|
387
|
+
throw new Error(`Login failed: ${response.statusText}`);
|
|
388
|
+
}
|
|
389
|
+
const data = await response.json();
|
|
390
|
+
return { user: data.user, token: data.token };
|
|
391
|
+
} catch (error) {
|
|
392
|
+
console.error("Login error:", error);
|
|
393
|
+
throw error;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function logout(token) {
|
|
397
|
+
const config = getConfig();
|
|
398
|
+
const authToken = token || getEnv3("DO_TOKEN") || "";
|
|
399
|
+
if (!authToken) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
try {
|
|
403
|
+
const response = await config.fetch(`${config.apiUrl}/logout`, {
|
|
404
|
+
method: "POST",
|
|
405
|
+
headers: {
|
|
406
|
+
"Authorization": `Bearer ${authToken}`,
|
|
407
|
+
"Content-Type": "application/json"
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
if (!response.ok) {
|
|
411
|
+
console.warn(`Logout warning: ${response.statusText}`);
|
|
412
|
+
}
|
|
413
|
+
} catch (error) {
|
|
414
|
+
console.error("Logout error:", error);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
async function getToken() {
|
|
418
|
+
const adminToken = getEnv3("DO_ADMIN_TOKEN");
|
|
419
|
+
if (adminToken) return adminToken;
|
|
420
|
+
const doToken = getEnv3("DO_TOKEN");
|
|
421
|
+
if (doToken) return doToken;
|
|
422
|
+
try {
|
|
423
|
+
const { createSecureStorage: createSecureStorage2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
424
|
+
const storage = createSecureStorage2();
|
|
425
|
+
return await storage.getToken();
|
|
426
|
+
} catch {
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
async function isAuthenticated(token) {
|
|
431
|
+
const result = await auth(token);
|
|
432
|
+
return result.user !== null;
|
|
433
|
+
}
|
|
434
|
+
function buildAuthUrl(options) {
|
|
435
|
+
const config = getConfig();
|
|
436
|
+
const clientId = options.clientId || config.clientId;
|
|
437
|
+
const authDomain = options.authDomain || config.authKitDomain;
|
|
438
|
+
const params = new URLSearchParams({
|
|
439
|
+
client_id: clientId,
|
|
440
|
+
redirect_uri: options.redirectUri,
|
|
441
|
+
response_type: options.responseType || "code",
|
|
442
|
+
scope: options.scope || "openid profile email"
|
|
443
|
+
});
|
|
444
|
+
if (options.state) {
|
|
445
|
+
params.set("state", options.state);
|
|
446
|
+
}
|
|
447
|
+
return `https://${authDomain}/authorize?${params.toString()}`;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/device.ts
|
|
451
|
+
async function authorizeDevice() {
|
|
452
|
+
const config = getConfig();
|
|
453
|
+
if (!config.clientId) {
|
|
454
|
+
throw new Error('Client ID is required for device authorization. Set OAUTH_CLIENT_ID or configure({ clientId: "..." })');
|
|
455
|
+
}
|
|
456
|
+
try {
|
|
457
|
+
const response = await config.fetch(`https://${config.authKitDomain}/device/authorize`, {
|
|
458
|
+
method: "POST",
|
|
459
|
+
headers: {
|
|
460
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
461
|
+
},
|
|
462
|
+
body: new URLSearchParams({
|
|
463
|
+
client_id: config.clientId
|
|
464
|
+
})
|
|
465
|
+
});
|
|
466
|
+
if (!response.ok) {
|
|
467
|
+
throw new Error(`Device authorization failed: ${response.statusText}`);
|
|
468
|
+
}
|
|
469
|
+
const data = await response.json();
|
|
470
|
+
return data;
|
|
471
|
+
} catch (error) {
|
|
472
|
+
console.error("Device authorization error:", error);
|
|
473
|
+
throw error;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
async function pollForTokens(deviceCode, interval = 5, expiresIn = 600) {
|
|
477
|
+
const config = getConfig();
|
|
478
|
+
if (!config.clientId) {
|
|
479
|
+
throw new Error("Client ID is required for token polling");
|
|
480
|
+
}
|
|
481
|
+
const startTime = Date.now();
|
|
482
|
+
const timeout = expiresIn * 1e3;
|
|
483
|
+
let currentInterval = interval * 1e3;
|
|
484
|
+
while (true) {
|
|
485
|
+
if (Date.now() - startTime > timeout) {
|
|
486
|
+
throw new Error("Device authorization expired. Please try again.");
|
|
487
|
+
}
|
|
488
|
+
await new Promise((resolve) => setTimeout(resolve, currentInterval));
|
|
489
|
+
try {
|
|
490
|
+
const response = await config.fetch(`https://${config.authKitDomain}/device/token`, {
|
|
491
|
+
method: "POST",
|
|
492
|
+
headers: {
|
|
493
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
494
|
+
},
|
|
495
|
+
body: new URLSearchParams({
|
|
496
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
497
|
+
device_code: deviceCode,
|
|
498
|
+
client_id: config.clientId
|
|
499
|
+
})
|
|
500
|
+
});
|
|
501
|
+
if (response.ok) {
|
|
502
|
+
const data = await response.json();
|
|
503
|
+
return data;
|
|
504
|
+
}
|
|
505
|
+
const errorData = await response.json().catch(() => ({ error: "unknown" }));
|
|
506
|
+
const error = errorData.error || "unknown";
|
|
507
|
+
switch (error) {
|
|
508
|
+
case "authorization_pending":
|
|
509
|
+
continue;
|
|
510
|
+
case "slow_down":
|
|
511
|
+
currentInterval += 5e3;
|
|
512
|
+
continue;
|
|
513
|
+
case "access_denied":
|
|
514
|
+
throw new Error("Access denied by user");
|
|
515
|
+
case "expired_token":
|
|
516
|
+
throw new Error("Device code expired");
|
|
517
|
+
default:
|
|
518
|
+
throw new Error(`Token polling failed: ${error}`);
|
|
519
|
+
}
|
|
520
|
+
} catch (error) {
|
|
521
|
+
if (error instanceof Error) {
|
|
522
|
+
throw error;
|
|
523
|
+
}
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// src/apikey.ts
|
|
530
|
+
function getEnv4(key) {
|
|
531
|
+
if (globalThis[key]) return globalThis[key];
|
|
532
|
+
if (typeof process !== "undefined" && process.env?.[key]) return process.env[key];
|
|
533
|
+
return void 0;
|
|
534
|
+
}
|
|
535
|
+
async function createApiKey(options, token) {
|
|
536
|
+
const config = getConfig();
|
|
537
|
+
const authToken = token || getEnv4("DO_TOKEN");
|
|
538
|
+
if (!authToken) {
|
|
539
|
+
throw new Error("Authentication required to create API key");
|
|
540
|
+
}
|
|
541
|
+
const response = await config.fetch(`${config.apiUrl}/api-keys`, {
|
|
542
|
+
method: "POST",
|
|
543
|
+
headers: {
|
|
544
|
+
"Authorization": `Bearer ${authToken}`,
|
|
545
|
+
"Content-Type": "application/json"
|
|
546
|
+
},
|
|
547
|
+
body: JSON.stringify(options)
|
|
548
|
+
});
|
|
549
|
+
if (!response.ok) {
|
|
550
|
+
const error = await response.text();
|
|
551
|
+
throw new Error(`Failed to create API key: ${error}`);
|
|
552
|
+
}
|
|
553
|
+
return response.json();
|
|
554
|
+
}
|
|
555
|
+
async function listApiKeys(token) {
|
|
556
|
+
const config = getConfig();
|
|
557
|
+
const authToken = token || getEnv4("DO_TOKEN");
|
|
558
|
+
if (!authToken) {
|
|
559
|
+
throw new Error("Authentication required to list API keys");
|
|
560
|
+
}
|
|
561
|
+
const response = await config.fetch(`${config.apiUrl}/api-keys`, {
|
|
562
|
+
method: "GET",
|
|
563
|
+
headers: {
|
|
564
|
+
"Authorization": `Bearer ${authToken}`,
|
|
565
|
+
"Content-Type": "application/json"
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
if (!response.ok) {
|
|
569
|
+
const error = await response.text();
|
|
570
|
+
throw new Error(`Failed to list API keys: ${error}`);
|
|
571
|
+
}
|
|
572
|
+
return response.json();
|
|
573
|
+
}
|
|
574
|
+
async function getApiKey(id, token) {
|
|
575
|
+
const config = getConfig();
|
|
576
|
+
const authToken = token || getEnv4("DO_TOKEN");
|
|
577
|
+
if (!authToken) {
|
|
578
|
+
throw new Error("Authentication required to get API key");
|
|
579
|
+
}
|
|
580
|
+
const response = await config.fetch(`${config.apiUrl}/api-keys/${id}`, {
|
|
581
|
+
method: "GET",
|
|
582
|
+
headers: {
|
|
583
|
+
"Authorization": `Bearer ${authToken}`,
|
|
584
|
+
"Content-Type": "application/json"
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
if (!response.ok) {
|
|
588
|
+
const error = await response.text();
|
|
589
|
+
throw new Error(`Failed to get API key: ${error}`);
|
|
590
|
+
}
|
|
591
|
+
return response.json();
|
|
592
|
+
}
|
|
593
|
+
async function rotateApiKey(id, options, token) {
|
|
594
|
+
const config = getConfig();
|
|
595
|
+
const authToken = token || getEnv4("DO_TOKEN");
|
|
596
|
+
if (!authToken) {
|
|
597
|
+
throw new Error("Authentication required to rotate API key");
|
|
598
|
+
}
|
|
599
|
+
const response = await config.fetch(`${config.apiUrl}/api-keys/${id}/rotate`, {
|
|
600
|
+
method: "POST",
|
|
601
|
+
headers: {
|
|
602
|
+
"Authorization": `Bearer ${authToken}`,
|
|
603
|
+
"Content-Type": "application/json"
|
|
604
|
+
},
|
|
605
|
+
body: JSON.stringify(options || {})
|
|
606
|
+
});
|
|
607
|
+
if (!response.ok) {
|
|
608
|
+
const error = await response.text();
|
|
609
|
+
throw new Error(`Failed to rotate API key: ${error}`);
|
|
610
|
+
}
|
|
611
|
+
return response.json();
|
|
612
|
+
}
|
|
613
|
+
async function deleteApiKey(id, token) {
|
|
614
|
+
const config = getConfig();
|
|
615
|
+
const authToken = token || getEnv4("DO_TOKEN");
|
|
616
|
+
if (!authToken) {
|
|
617
|
+
throw new Error("Authentication required to delete API key");
|
|
618
|
+
}
|
|
619
|
+
const response = await config.fetch(`${config.apiUrl}/api-keys/${id}`, {
|
|
620
|
+
method: "DELETE",
|
|
621
|
+
headers: {
|
|
622
|
+
"Authorization": `Bearer ${authToken}`,
|
|
623
|
+
"Content-Type": "application/json"
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
if (!response.ok) {
|
|
627
|
+
const error = await response.text();
|
|
628
|
+
throw new Error(`Failed to delete API key: ${error}`);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// src/index.ts
|
|
633
|
+
init_storage();
|
|
634
|
+
|
|
635
|
+
export { CompositeTokenStorage, FileTokenStorage, KeychainTokenStorage, LocalStorageTokenStorage, MemoryTokenStorage, SecureFileTokenStorage, auth, authorizeDevice, buildAuthUrl, configure, createApiKey, createSecureStorage, deleteApiKey, getApiKey, getConfig, getToken, isAuthenticated, listApiKeys, login, logout, pollForTokens, rotateApiKey };
|
|
636
|
+
//# sourceMappingURL=index.js.map
|
|
637
|
+
//# sourceMappingURL=index.js.map
|