@korajs/auth 0.4.0 → 0.6.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 +81 -28
- package/dist/chunk-7OXBRSJL.js +203 -0
- package/dist/chunk-7OXBRSJL.js.map +1 -0
- package/dist/create-org-session-RsDj9cl4.d.cts +722 -0
- package/dist/create-org-session-RsDj9cl4.d.ts +722 -0
- package/dist/index.cjs +1146 -586
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +67 -118
- package/dist/index.d.ts +67 -118
- package/dist/index.js +654 -258
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +297 -183
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +25 -209
- package/dist/react.d.ts +25 -209
- package/dist/react.js +134 -209
- package/dist/react.js.map +1 -1
- package/dist/server.cjs +1611 -730
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +415 -224
- package/dist/server.d.ts +415 -224
- package/dist/server.js +1565 -704
- package/dist/server.js.map +1 -1
- package/dist/svelte.cjs +512 -0
- package/dist/svelte.cjs.map +1 -0
- package/dist/svelte.d.cts +100 -0
- package/dist/svelte.d.ts +100 -0
- package/dist/svelte.js +278 -0
- package/dist/svelte.js.map +1 -0
- package/dist/vue.cjs +565 -0
- package/dist/vue.cjs.map +1 -0
- package/dist/vue.d.cts +136 -0
- package/dist/vue.d.ts +136 -0
- package/dist/vue.js +338 -0
- package/dist/vue.js.map +1 -0
- package/package.json +40 -6
- package/src/svelte/AuthProvider.svelte +37 -0
- package/src/svelte/OrgProvider.svelte +22 -0
- package/dist/org-client-BVTLKcIk.d.cts +0 -355
- package/dist/org-client-BVTLKcIk.d.ts +0 -355
package/dist/index.js
CHANGED
|
@@ -24,6 +24,11 @@ import {
|
|
|
24
24
|
toBase64Url,
|
|
25
25
|
verifyChallenge
|
|
26
26
|
} from "./chunk-IO2MCCG2.js";
|
|
27
|
+
import {
|
|
28
|
+
checkOrgPermission,
|
|
29
|
+
createAuthSession,
|
|
30
|
+
createOrgSession
|
|
31
|
+
} from "./chunk-7OXBRSJL.js";
|
|
27
32
|
|
|
28
33
|
// src/client/auth-client.ts
|
|
29
34
|
import { KoraError } from "@korajs/core";
|
|
@@ -62,6 +67,36 @@ function getUserIdFromToken(token) {
|
|
|
62
67
|
}
|
|
63
68
|
return payload.sub;
|
|
64
69
|
}
|
|
70
|
+
function getDefaultFetch() {
|
|
71
|
+
if (typeof globalThis.fetch !== "function") {
|
|
72
|
+
return async () => {
|
|
73
|
+
throw new AuthError(
|
|
74
|
+
"No fetch implementation is available in this runtime. Pass `fetch` to AuthClientConfig.",
|
|
75
|
+
"AUTH_FETCH_UNAVAILABLE"
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return globalThis.fetch.bind(globalThis);
|
|
80
|
+
}
|
|
81
|
+
function normalizeAuthUser(user) {
|
|
82
|
+
return {
|
|
83
|
+
id: user.id,
|
|
84
|
+
email: user.email,
|
|
85
|
+
name: user.name ?? null
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function canRedirectCurrentWindow() {
|
|
89
|
+
return typeof globalThis.window !== "undefined" && typeof globalThis.window.location?.assign === "function";
|
|
90
|
+
}
|
|
91
|
+
function redirectCurrentWindow(url) {
|
|
92
|
+
if (!canRedirectCurrentWindow()) {
|
|
93
|
+
throw new AuthError(
|
|
94
|
+
"OAuth redirect is not available in this runtime. Pass redirect: false and open the returned URL with your platform browser API.",
|
|
95
|
+
"AUTH_OAUTH_REDIRECT_UNAVAILABLE"
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
globalThis.window.location.assign(url);
|
|
99
|
+
}
|
|
65
100
|
function createTokenStorage(prefix) {
|
|
66
101
|
let useLocalStorage = false;
|
|
67
102
|
try {
|
|
@@ -115,6 +150,8 @@ function createTokenStorage(prefix) {
|
|
|
115
150
|
var AuthClient = class {
|
|
116
151
|
serverUrl;
|
|
117
152
|
storage;
|
|
153
|
+
fetchFn;
|
|
154
|
+
deviceIdentity;
|
|
118
155
|
listeners = /* @__PURE__ */ new Set();
|
|
119
156
|
_state = "loading";
|
|
120
157
|
_user = null;
|
|
@@ -128,7 +165,9 @@ var AuthClient = class {
|
|
|
128
165
|
constructor(config) {
|
|
129
166
|
this.serverUrl = config.serverUrl.replace(/\/+$/, "");
|
|
130
167
|
const prefix = config.storageKey ?? "kora_auth";
|
|
131
|
-
this.storage = createTokenStorage(prefix);
|
|
168
|
+
this.storage = config.storage ?? createTokenStorage(prefix);
|
|
169
|
+
this.fetchFn = config.fetch ?? getDefaultFetch();
|
|
170
|
+
this.deviceIdentity = config.deviceIdentity;
|
|
132
171
|
}
|
|
133
172
|
// -----------------------------------------------------------------------
|
|
134
173
|
// Public getters
|
|
@@ -160,8 +199,8 @@ var AuthClient = class {
|
|
|
160
199
|
return;
|
|
161
200
|
}
|
|
162
201
|
this._initialized = true;
|
|
163
|
-
const accessToken = this.storage.getAccessToken();
|
|
164
|
-
const refreshToken = this.storage.getRefreshToken();
|
|
202
|
+
const accessToken = await this.storage.getAccessToken();
|
|
203
|
+
const refreshToken = await this.storage.getRefreshToken();
|
|
165
204
|
if (!accessToken || !refreshToken) {
|
|
166
205
|
this.setState("unauthenticated", null);
|
|
167
206
|
return;
|
|
@@ -178,7 +217,7 @@ var AuthClient = class {
|
|
|
178
217
|
}
|
|
179
218
|
} catch {
|
|
180
219
|
}
|
|
181
|
-
this.storage.clear();
|
|
220
|
+
await this.storage.clear();
|
|
182
221
|
this.setState("unauthenticated", null);
|
|
183
222
|
}
|
|
184
223
|
// -----------------------------------------------------------------------
|
|
@@ -192,12 +231,13 @@ var AuthClient = class {
|
|
|
192
231
|
* @throws {AuthError} If the request fails or the server returns an error
|
|
193
232
|
*/
|
|
194
233
|
async signUp(params) {
|
|
234
|
+
const body = await this.withDeviceIdentity(params);
|
|
195
235
|
const response = await this.request("/auth/signup", {
|
|
196
236
|
method: "POST",
|
|
197
|
-
body
|
|
237
|
+
body
|
|
198
238
|
});
|
|
199
239
|
const tokens = "tokens" in response ? response.tokens : response;
|
|
200
|
-
this.storage.setTokens(tokens.accessToken, tokens.refreshToken);
|
|
240
|
+
await this.storage.setTokens(tokens.accessToken, tokens.refreshToken);
|
|
201
241
|
const user = await this.fetchUserProfile(tokens.accessToken);
|
|
202
242
|
this.setState("authenticated", user);
|
|
203
243
|
return user;
|
|
@@ -210,16 +250,88 @@ var AuthClient = class {
|
|
|
210
250
|
* @throws {AuthError} If the credentials are invalid or the request fails
|
|
211
251
|
*/
|
|
212
252
|
async signIn(params) {
|
|
253
|
+
const body = await this.withDeviceIdentity(params);
|
|
213
254
|
const response = await this.request("/auth/signin", {
|
|
214
255
|
method: "POST",
|
|
215
|
-
body
|
|
256
|
+
body
|
|
216
257
|
});
|
|
217
258
|
const tokens = "tokens" in response ? response.tokens : response;
|
|
218
|
-
this.storage.setTokens(tokens.accessToken, tokens.refreshToken);
|
|
259
|
+
await this.storage.setTokens(tokens.accessToken, tokens.refreshToken);
|
|
219
260
|
const user = await this.fetchUserProfile(tokens.accessToken);
|
|
220
261
|
this.setState("authenticated", user);
|
|
221
262
|
return user;
|
|
222
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* Create an OAuth authorization URL and optionally redirect the current window.
|
|
266
|
+
*
|
|
267
|
+
* For web apps, call this from a button click and keep the default redirect behavior.
|
|
268
|
+
* For desktop/mobile, pass `redirect: false`, open the returned URL with the runtime's
|
|
269
|
+
* browser API, then call `completeOAuthSignIn()` after receiving the callback.
|
|
270
|
+
*/
|
|
271
|
+
async signInWithOAuth(provider, options = {}) {
|
|
272
|
+
const result = await this.createOAuthAuthorization(provider, options);
|
|
273
|
+
if (options.redirect ?? canRedirectCurrentWindow()) {
|
|
274
|
+
redirectCurrentWindow(result.url);
|
|
275
|
+
}
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Complete an OAuth sign-in callback and store the issued Kora tokens.
|
|
280
|
+
*/
|
|
281
|
+
async completeOAuthSignIn(provider, params) {
|
|
282
|
+
const body = await this.withDeviceIdentity(params);
|
|
283
|
+
const response = await this.request(
|
|
284
|
+
`/auth/oauth/${encodeURIComponent(provider)}/callback`,
|
|
285
|
+
{
|
|
286
|
+
method: "POST",
|
|
287
|
+
body: { ...body }
|
|
288
|
+
}
|
|
289
|
+
);
|
|
290
|
+
await this.storage.setTokens(response.tokens.accessToken, response.tokens.refreshToken);
|
|
291
|
+
const user = normalizeAuthUser(response.user);
|
|
292
|
+
this.setState("authenticated", user);
|
|
293
|
+
return user;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Create an OAuth authorization URL for linking another provider to the current user.
|
|
297
|
+
*/
|
|
298
|
+
async getOAuthAuthorizationUrl(provider, options = {}) {
|
|
299
|
+
return this.createOAuthAuthorization(provider, { ...options, redirect: false });
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Link an OAuth provider to the current authenticated user.
|
|
303
|
+
*/
|
|
304
|
+
async linkOAuth(provider, params) {
|
|
305
|
+
const token = await this.requireAccessToken();
|
|
306
|
+
return this.request(`/auth/oauth/${encodeURIComponent(provider)}/link`, {
|
|
307
|
+
method: "POST",
|
|
308
|
+
body: {
|
|
309
|
+
code: params.code,
|
|
310
|
+
state: params.state
|
|
311
|
+
},
|
|
312
|
+
token
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* List OAuth accounts linked to the current authenticated user.
|
|
317
|
+
*/
|
|
318
|
+
async listLinkedAccounts() {
|
|
319
|
+
const token = await this.requireAccessToken();
|
|
320
|
+
return this.request("/auth/oauth/links", {
|
|
321
|
+
method: "GET",
|
|
322
|
+
token
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Unlink an OAuth provider from the current authenticated user.
|
|
327
|
+
*/
|
|
328
|
+
async unlinkOAuth(provider) {
|
|
329
|
+
const token = await this.requireAccessToken();
|
|
330
|
+
await this.request(`/auth/oauth/${encodeURIComponent(provider)}/link`, {
|
|
331
|
+
method: "DELETE",
|
|
332
|
+
token
|
|
333
|
+
});
|
|
334
|
+
}
|
|
223
335
|
/**
|
|
224
336
|
* Sign out the current user.
|
|
225
337
|
*
|
|
@@ -228,9 +340,9 @@ var AuthClient = class {
|
|
|
228
340
|
* stolen refresh tokens cannot be used after the user explicitly signs out.
|
|
229
341
|
*/
|
|
230
342
|
async signOut() {
|
|
231
|
-
const accessToken = this.storage.getAccessToken();
|
|
232
|
-
const refreshToken = this.storage.getRefreshToken();
|
|
233
|
-
this.storage.clear();
|
|
343
|
+
const accessToken = await this.storage.getAccessToken();
|
|
344
|
+
const refreshToken = await this.storage.getRefreshToken();
|
|
345
|
+
await this.storage.clear();
|
|
234
346
|
this._refreshPromise = null;
|
|
235
347
|
this.setState("unauthenticated", null);
|
|
236
348
|
if (accessToken) {
|
|
@@ -254,11 +366,11 @@ var AuthClient = class {
|
|
|
254
366
|
* authenticated and refresh is not possible
|
|
255
367
|
*/
|
|
256
368
|
async getAccessToken() {
|
|
257
|
-
const accessToken = this.storage.getAccessToken();
|
|
369
|
+
const accessToken = await this.storage.getAccessToken();
|
|
258
370
|
if (accessToken && !isTokenExpired(accessToken)) {
|
|
259
371
|
return accessToken;
|
|
260
372
|
}
|
|
261
|
-
const refreshToken = this.storage.getRefreshToken();
|
|
373
|
+
const refreshToken = await this.storage.getRefreshToken();
|
|
262
374
|
if (!refreshToken) {
|
|
263
375
|
return null;
|
|
264
376
|
}
|
|
@@ -341,7 +453,7 @@ var AuthClient = class {
|
|
|
341
453
|
name: null
|
|
342
454
|
});
|
|
343
455
|
} else {
|
|
344
|
-
this.storage.clear();
|
|
456
|
+
await this.storage.clear();
|
|
345
457
|
this.setState("unauthenticated", null);
|
|
346
458
|
}
|
|
347
459
|
}
|
|
@@ -354,10 +466,47 @@ var AuthClient = class {
|
|
|
354
466
|
method: "GET",
|
|
355
467
|
token: accessToken
|
|
356
468
|
});
|
|
469
|
+
return normalizeAuthUser(profile);
|
|
470
|
+
}
|
|
471
|
+
async createOAuthAuthorization(provider, options) {
|
|
472
|
+
const params = new URLSearchParams();
|
|
473
|
+
const withIdentity = await this.withDeviceIdentity({
|
|
474
|
+
deviceId: options.deviceId,
|
|
475
|
+
devicePublicKey: options.devicePublicKey
|
|
476
|
+
});
|
|
477
|
+
if (options.returnTo) {
|
|
478
|
+
params.set("returnTo", options.returnTo);
|
|
479
|
+
}
|
|
480
|
+
if (withIdentity.deviceId) {
|
|
481
|
+
params.set("deviceId", withIdentity.deviceId);
|
|
482
|
+
}
|
|
483
|
+
if (withIdentity.devicePublicKey) {
|
|
484
|
+
params.set("devicePublicKey", withIdentity.devicePublicKey);
|
|
485
|
+
}
|
|
486
|
+
if (options.metadata) {
|
|
487
|
+
for (const [key, value] of Object.entries(options.metadata)) {
|
|
488
|
+
if (value !== void 0 && value !== null) {
|
|
489
|
+
params.set(key, String(value));
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
const query = params.toString();
|
|
494
|
+
return this.request(
|
|
495
|
+
`/auth/oauth/${encodeURIComponent(provider)}${query ? `?${query}` : ""}`,
|
|
496
|
+
{
|
|
497
|
+
method: "GET"
|
|
498
|
+
}
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
async withDeviceIdentity(params) {
|
|
502
|
+
if (!this.deviceIdentity || params.deviceId && params.devicePublicKey) {
|
|
503
|
+
return params;
|
|
504
|
+
}
|
|
505
|
+
const identity = await this.deviceIdentity.getDeviceIdentity();
|
|
357
506
|
return {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
507
|
+
...params,
|
|
508
|
+
deviceId: params.deviceId ?? identity.deviceId,
|
|
509
|
+
devicePublicKey: params.devicePublicKey ?? identity.devicePublicKey
|
|
361
510
|
};
|
|
362
511
|
}
|
|
363
512
|
/**
|
|
@@ -376,6 +525,13 @@ var AuthClient = class {
|
|
|
376
525
|
this._refreshPromise = null;
|
|
377
526
|
}
|
|
378
527
|
}
|
|
528
|
+
async requireAccessToken() {
|
|
529
|
+
const token = await this.getAccessToken();
|
|
530
|
+
if (!token) {
|
|
531
|
+
throw new AuthError("You must be signed in to perform this action.", "AUTH_REQUIRED");
|
|
532
|
+
}
|
|
533
|
+
return token;
|
|
534
|
+
}
|
|
379
535
|
/**
|
|
380
536
|
* Execute the token refresh network request.
|
|
381
537
|
*/
|
|
@@ -385,10 +541,10 @@ var AuthClient = class {
|
|
|
385
541
|
method: "POST",
|
|
386
542
|
body: { refreshToken }
|
|
387
543
|
});
|
|
388
|
-
this.storage.setTokens(response.accessToken, response.refreshToken);
|
|
544
|
+
await this.storage.setTokens(response.accessToken, response.refreshToken);
|
|
389
545
|
return response.accessToken;
|
|
390
546
|
} catch {
|
|
391
|
-
this.storage.clear();
|
|
547
|
+
await this.storage.clear();
|
|
392
548
|
this.setState("unauthenticated", null);
|
|
393
549
|
return null;
|
|
394
550
|
}
|
|
@@ -412,7 +568,7 @@ var AuthClient = class {
|
|
|
412
568
|
}
|
|
413
569
|
let response;
|
|
414
570
|
try {
|
|
415
|
-
response = await
|
|
571
|
+
response = await this.fetchFn(url, {
|
|
416
572
|
method: options.method,
|
|
417
573
|
headers,
|
|
418
574
|
body: options.body ? JSON.stringify(options.body) : void 0
|
|
@@ -424,35 +580,475 @@ var AuthClient = class {
|
|
|
424
580
|
{ path, cause: cause instanceof Error ? cause.message : String(cause) }
|
|
425
581
|
);
|
|
426
582
|
}
|
|
427
|
-
if (!response.ok) {
|
|
428
|
-
let errorMessage = `Auth server returned HTTP ${response.status}`;
|
|
429
|
-
let serverError;
|
|
430
|
-
try {
|
|
431
|
-
const body = await response.json();
|
|
432
|
-
if (typeof body.error === "string") {
|
|
433
|
-
errorMessage = body.error;
|
|
434
|
-
serverError = errorMessage;
|
|
435
|
-
} else if (typeof body.message === "string") {
|
|
436
|
-
errorMessage = body.message;
|
|
437
|
-
serverError = errorMessage;
|
|
438
|
-
}
|
|
439
|
-
} catch {
|
|
583
|
+
if (!response.ok) {
|
|
584
|
+
let errorMessage = `Auth server returned HTTP ${response.status}`;
|
|
585
|
+
let serverError;
|
|
586
|
+
try {
|
|
587
|
+
const body = await response.json();
|
|
588
|
+
if (typeof body.error === "string") {
|
|
589
|
+
errorMessage = body.error;
|
|
590
|
+
serverError = errorMessage;
|
|
591
|
+
} else if (typeof body.message === "string") {
|
|
592
|
+
errorMessage = body.message;
|
|
593
|
+
serverError = errorMessage;
|
|
594
|
+
}
|
|
595
|
+
} catch {
|
|
596
|
+
}
|
|
597
|
+
throw new AuthError(errorMessage, "AUTH_SERVER_ERROR", {
|
|
598
|
+
path,
|
|
599
|
+
status: response.status,
|
|
600
|
+
serverError
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
const json = await response.json();
|
|
604
|
+
const data = json.data !== void 0 ? json.data : json;
|
|
605
|
+
return data;
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
// src/client/device-session.ts
|
|
610
|
+
import { KoraError as KoraError3 } from "@korajs/core";
|
|
611
|
+
|
|
612
|
+
// src/device/device-store.ts
|
|
613
|
+
import { KoraError as KoraError2 } from "@korajs/core";
|
|
614
|
+
var DeviceKeyStoreError = class extends KoraError2 {
|
|
615
|
+
constructor(message, context) {
|
|
616
|
+
super(message, "DEVICE_KEY_STORE_ERROR", context);
|
|
617
|
+
this.name = "DeviceKeyStoreError";
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
var IDB_DATABASE_NAME = "kora_device_keys";
|
|
621
|
+
var IDB_STORE_NAME = "keypairs";
|
|
622
|
+
var IDB_VERSION = 1;
|
|
623
|
+
var IndexedDBDeviceKeyStore = class {
|
|
624
|
+
dbPromise = null;
|
|
625
|
+
/**
|
|
626
|
+
* Opens (or creates) the IndexedDB database.
|
|
627
|
+
*
|
|
628
|
+
* The database connection is lazily initialized on first use and
|
|
629
|
+
* reused for subsequent operations. If the database does not exist,
|
|
630
|
+
* it is created with the `keypairs` object store.
|
|
631
|
+
*/
|
|
632
|
+
openDatabase() {
|
|
633
|
+
if (this.dbPromise !== null) {
|
|
634
|
+
return this.dbPromise;
|
|
635
|
+
}
|
|
636
|
+
this.dbPromise = new Promise((resolve, reject) => {
|
|
637
|
+
let request;
|
|
638
|
+
try {
|
|
639
|
+
request = globalThis.indexedDB.open(IDB_DATABASE_NAME, IDB_VERSION);
|
|
640
|
+
} catch (cause) {
|
|
641
|
+
this.dbPromise = null;
|
|
642
|
+
reject(
|
|
643
|
+
new DeviceKeyStoreError(
|
|
644
|
+
"Failed to open IndexedDB database for device key storage. IndexedDB may be unavailable or access may be denied.",
|
|
645
|
+
{ cause: cause instanceof Error ? cause.message : String(cause) }
|
|
646
|
+
)
|
|
647
|
+
);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
request.onupgradeneeded = () => {
|
|
651
|
+
const db = request.result;
|
|
652
|
+
if (!db.objectStoreNames.contains(IDB_STORE_NAME)) {
|
|
653
|
+
db.createObjectStore(IDB_STORE_NAME);
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
request.onsuccess = () => {
|
|
657
|
+
resolve(request.result);
|
|
658
|
+
};
|
|
659
|
+
request.onerror = () => {
|
|
660
|
+
this.dbPromise = null;
|
|
661
|
+
reject(
|
|
662
|
+
new DeviceKeyStoreError("Failed to open IndexedDB database for device key storage.", {
|
|
663
|
+
error: request.error?.message
|
|
664
|
+
})
|
|
665
|
+
);
|
|
666
|
+
};
|
|
667
|
+
request.onblocked = () => {
|
|
668
|
+
this.dbPromise = null;
|
|
669
|
+
reject(
|
|
670
|
+
new DeviceKeyStoreError(
|
|
671
|
+
"IndexedDB database open was blocked. Another tab may have an older version of the database open. Close other tabs and try again."
|
|
672
|
+
)
|
|
673
|
+
);
|
|
674
|
+
};
|
|
675
|
+
});
|
|
676
|
+
return this.dbPromise;
|
|
677
|
+
}
|
|
678
|
+
/** @inheritdoc */
|
|
679
|
+
async saveKeyPair(deviceId, keyPair) {
|
|
680
|
+
const db = await this.openDatabase();
|
|
681
|
+
return new Promise((resolve, reject) => {
|
|
682
|
+
try {
|
|
683
|
+
const tx = db.transaction(IDB_STORE_NAME, "readwrite");
|
|
684
|
+
const store = tx.objectStore(IDB_STORE_NAME);
|
|
685
|
+
store.put(keyPair, deviceId);
|
|
686
|
+
tx.oncomplete = () => {
|
|
687
|
+
resolve();
|
|
688
|
+
};
|
|
689
|
+
tx.onerror = () => {
|
|
690
|
+
reject(
|
|
691
|
+
new DeviceKeyStoreError(`Failed to save key pair for device "${deviceId}".`, {
|
|
692
|
+
deviceId,
|
|
693
|
+
error: tx.error?.message
|
|
694
|
+
})
|
|
695
|
+
);
|
|
696
|
+
};
|
|
697
|
+
} catch (cause) {
|
|
698
|
+
reject(
|
|
699
|
+
new DeviceKeyStoreError(`Failed to save key pair for device "${deviceId}".`, {
|
|
700
|
+
deviceId,
|
|
701
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
702
|
+
})
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
/** @inheritdoc */
|
|
708
|
+
async loadKeyPair(deviceId) {
|
|
709
|
+
const db = await this.openDatabase();
|
|
710
|
+
return new Promise((resolve, reject) => {
|
|
711
|
+
try {
|
|
712
|
+
const tx = db.transaction(IDB_STORE_NAME, "readonly");
|
|
713
|
+
const store = tx.objectStore(IDB_STORE_NAME);
|
|
714
|
+
const request = store.get(deviceId);
|
|
715
|
+
request.onsuccess = () => {
|
|
716
|
+
const result = request.result;
|
|
717
|
+
resolve(result ?? null);
|
|
718
|
+
};
|
|
719
|
+
request.onerror = () => {
|
|
720
|
+
reject(
|
|
721
|
+
new DeviceKeyStoreError(`Failed to load key pair for device "${deviceId}".`, {
|
|
722
|
+
deviceId,
|
|
723
|
+
error: request.error?.message
|
|
724
|
+
})
|
|
725
|
+
);
|
|
726
|
+
};
|
|
727
|
+
} catch (cause) {
|
|
728
|
+
reject(
|
|
729
|
+
new DeviceKeyStoreError(`Failed to load key pair for device "${deviceId}".`, {
|
|
730
|
+
deviceId,
|
|
731
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
732
|
+
})
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
/** @inheritdoc */
|
|
738
|
+
async deleteKeyPair(deviceId) {
|
|
739
|
+
const db = await this.openDatabase();
|
|
740
|
+
return new Promise((resolve, reject) => {
|
|
741
|
+
try {
|
|
742
|
+
const tx = db.transaction(IDB_STORE_NAME, "readwrite");
|
|
743
|
+
const store = tx.objectStore(IDB_STORE_NAME);
|
|
744
|
+
store.delete(deviceId);
|
|
745
|
+
tx.oncomplete = () => {
|
|
746
|
+
resolve();
|
|
747
|
+
};
|
|
748
|
+
tx.onerror = () => {
|
|
749
|
+
reject(
|
|
750
|
+
new DeviceKeyStoreError(`Failed to delete key pair for device "${deviceId}".`, {
|
|
751
|
+
deviceId,
|
|
752
|
+
error: tx.error?.message
|
|
753
|
+
})
|
|
754
|
+
);
|
|
755
|
+
};
|
|
756
|
+
} catch (cause) {
|
|
757
|
+
reject(
|
|
758
|
+
new DeviceKeyStoreError(`Failed to delete key pair for device "${deviceId}".`, {
|
|
759
|
+
deviceId,
|
|
760
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
761
|
+
})
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
/** @inheritdoc */
|
|
767
|
+
async hasKeyPair(deviceId) {
|
|
768
|
+
const db = await this.openDatabase();
|
|
769
|
+
return new Promise((resolve, reject) => {
|
|
770
|
+
try {
|
|
771
|
+
const tx = db.transaction(IDB_STORE_NAME, "readonly");
|
|
772
|
+
const store = tx.objectStore(IDB_STORE_NAME);
|
|
773
|
+
const request = store.count(deviceId);
|
|
774
|
+
request.onsuccess = () => {
|
|
775
|
+
resolve(request.result > 0);
|
|
776
|
+
};
|
|
777
|
+
request.onerror = () => {
|
|
778
|
+
reject(
|
|
779
|
+
new DeviceKeyStoreError(
|
|
780
|
+
`Failed to check if key pair exists for device "${deviceId}".`,
|
|
781
|
+
{ deviceId, error: request.error?.message }
|
|
782
|
+
)
|
|
783
|
+
);
|
|
784
|
+
};
|
|
785
|
+
} catch (cause) {
|
|
786
|
+
reject(
|
|
787
|
+
new DeviceKeyStoreError(`Failed to check if key pair exists for device "${deviceId}".`, {
|
|
788
|
+
deviceId,
|
|
789
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
790
|
+
})
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
var InMemoryDeviceKeyStore = class {
|
|
797
|
+
store = /* @__PURE__ */ new Map();
|
|
798
|
+
/** @inheritdoc */
|
|
799
|
+
async saveKeyPair(deviceId, keyPair) {
|
|
800
|
+
this.store.set(deviceId, keyPair);
|
|
801
|
+
}
|
|
802
|
+
/** @inheritdoc */
|
|
803
|
+
async loadKeyPair(deviceId) {
|
|
804
|
+
return this.store.get(deviceId) ?? null;
|
|
805
|
+
}
|
|
806
|
+
/** @inheritdoc */
|
|
807
|
+
async deleteKeyPair(deviceId) {
|
|
808
|
+
this.store.delete(deviceId);
|
|
809
|
+
}
|
|
810
|
+
/** @inheritdoc */
|
|
811
|
+
async hasKeyPair(deviceId) {
|
|
812
|
+
return this.store.has(deviceId);
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
function createDeviceKeyStore() {
|
|
816
|
+
if (typeof globalThis.indexedDB !== "undefined") {
|
|
817
|
+
return new IndexedDBDeviceKeyStore();
|
|
818
|
+
}
|
|
819
|
+
return new InMemoryDeviceKeyStore();
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// src/client/device-session.ts
|
|
823
|
+
var DEFAULT_DEVICE_ID_KEY = "kora_auth_device_id";
|
|
824
|
+
var AuthDeviceIdentityError = class extends KoraError3 {
|
|
825
|
+
constructor(message, context) {
|
|
826
|
+
super(message, "AUTH_DEVICE_IDENTITY_ERROR", context);
|
|
827
|
+
this.name = "AuthDeviceIdentityError";
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
function createPersistentDeviceIdentity(options) {
|
|
831
|
+
const storage = options.storage;
|
|
832
|
+
const keyStore = options.keyStore ?? createDefaultPersistentKeyStore();
|
|
833
|
+
const deviceIdKey = options.deviceIdKey ?? DEFAULT_DEVICE_ID_KEY;
|
|
834
|
+
const generateDeviceId = options.generateDeviceId ?? defaultDeviceId;
|
|
835
|
+
return {
|
|
836
|
+
async getDeviceIdentity() {
|
|
837
|
+
let deviceId = await storage.getItem(deviceIdKey);
|
|
838
|
+
if (!deviceId) {
|
|
839
|
+
deviceId = generateDeviceId();
|
|
840
|
+
await storage.setItem(deviceIdKey, deviceId);
|
|
841
|
+
}
|
|
842
|
+
let keyPair = await keyStore.loadKeyPair(deviceId);
|
|
843
|
+
if (!keyPair) {
|
|
844
|
+
keyPair = await generateDeviceKeyPair();
|
|
845
|
+
await keyStore.saveKeyPair(deviceId, keyPair);
|
|
846
|
+
}
|
|
847
|
+
const publicKey = await exportPublicKeyJwk(keyPair);
|
|
848
|
+
return {
|
|
849
|
+
deviceId,
|
|
850
|
+
devicePublicKey: JSON.stringify(publicKey)
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
function createDefaultPersistentKeyStore() {
|
|
856
|
+
if (typeof globalThis.indexedDB !== "undefined") {
|
|
857
|
+
return createDeviceKeyStore();
|
|
858
|
+
}
|
|
859
|
+
throw new AuthDeviceIdentityError(
|
|
860
|
+
"No persistent device key store is available in this runtime. Pass `keyStore` to createPersistentDeviceIdentity()."
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
function defaultDeviceId() {
|
|
864
|
+
if (typeof globalThis.crypto?.randomUUID === "function") {
|
|
865
|
+
return globalThis.crypto.randomUUID();
|
|
866
|
+
}
|
|
867
|
+
const bytes = new Uint8Array(16);
|
|
868
|
+
if (typeof globalThis.crypto?.getRandomValues === "function") {
|
|
869
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
870
|
+
} else {
|
|
871
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
872
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
876
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
877
|
+
const hex = [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
878
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// src/client/storage.ts
|
|
882
|
+
function createAuthTokenStorage(options) {
|
|
883
|
+
const prefix = options.prefix ?? "kora_auth";
|
|
884
|
+
const accessKey = `${prefix}_access_token`;
|
|
885
|
+
const refreshKey = `${prefix}_refresh_token`;
|
|
886
|
+
const store = options.store;
|
|
887
|
+
return {
|
|
888
|
+
getAccessToken: () => store.getItem(accessKey),
|
|
889
|
+
getRefreshToken: () => store.getItem(refreshKey),
|
|
890
|
+
async setTokens(accessToken, refreshToken) {
|
|
891
|
+
await store.setItem(accessKey, accessToken);
|
|
892
|
+
await store.setItem(refreshKey, refreshToken);
|
|
893
|
+
},
|
|
894
|
+
async clear() {
|
|
895
|
+
await store.removeItem(accessKey);
|
|
896
|
+
await store.removeItem(refreshKey);
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
function createMemoryAuthTokenStorage() {
|
|
901
|
+
let accessToken = null;
|
|
902
|
+
let refreshToken = null;
|
|
903
|
+
return {
|
|
904
|
+
getAccessToken: () => accessToken,
|
|
905
|
+
getRefreshToken: () => refreshToken,
|
|
906
|
+
setTokens(access, refresh) {
|
|
907
|
+
accessToken = access;
|
|
908
|
+
refreshToken = refresh;
|
|
909
|
+
},
|
|
910
|
+
clear() {
|
|
911
|
+
accessToken = null;
|
|
912
|
+
refreshToken = null;
|
|
913
|
+
}
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
function createWebStorageAuthTokenStorage(storage, prefix) {
|
|
917
|
+
return createAuthTokenStorage({
|
|
918
|
+
prefix,
|
|
919
|
+
store: {
|
|
920
|
+
getItem: (key) => storage.getItem(key),
|
|
921
|
+
setItem: (key, value) => {
|
|
922
|
+
storage.setItem(key, value);
|
|
923
|
+
},
|
|
924
|
+
removeItem: (key) => {
|
|
925
|
+
storage.removeItem(key);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// src/client/quickstart.ts
|
|
932
|
+
function createKoraAuth(options) {
|
|
933
|
+
const storage = options.storage ?? (options.credentialStore ? createAuthTokenStorage({
|
|
934
|
+
store: options.credentialStore,
|
|
935
|
+
prefix: options.storageKey
|
|
936
|
+
}) : tryCreateDefaultTokenStorage(options.storageKey));
|
|
937
|
+
const deviceIdentity = options.deviceIdentity === false ? void 0 : options.deviceIdentity ?? tryCreateDefaultDeviceIdentity(options.credentialStore, options.deviceKeyStore);
|
|
938
|
+
return new AuthClient({
|
|
939
|
+
serverUrl: options.serverUrl,
|
|
940
|
+
storageKey: options.storageKey,
|
|
941
|
+
storage,
|
|
942
|
+
fetch: options.fetch,
|
|
943
|
+
deviceIdentity
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
function tryCreateDefaultTokenStorage(storageKey) {
|
|
947
|
+
const storage = tryGetBrowserStorage();
|
|
948
|
+
return storage ? createWebStorageAuthTokenStorage(storage, storageKey) : void 0;
|
|
949
|
+
}
|
|
950
|
+
function tryCreateDefaultDeviceIdentity(credentialStore, deviceKeyStore) {
|
|
951
|
+
const storage = credentialStore ?? tryGetBrowserStorage();
|
|
952
|
+
if (!storage) {
|
|
953
|
+
return void 0;
|
|
954
|
+
}
|
|
955
|
+
try {
|
|
956
|
+
return createPersistentDeviceIdentity({
|
|
957
|
+
storage,
|
|
958
|
+
keyStore: deviceKeyStore
|
|
959
|
+
});
|
|
960
|
+
} catch {
|
|
961
|
+
return void 0;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
function tryGetBrowserStorage() {
|
|
965
|
+
try {
|
|
966
|
+
if (typeof globalThis.localStorage === "undefined") {
|
|
967
|
+
return null;
|
|
968
|
+
}
|
|
969
|
+
const key = "__kora_auth_quickstart_test__";
|
|
970
|
+
globalThis.localStorage.setItem(key, "1");
|
|
971
|
+
globalThis.localStorage.removeItem(key);
|
|
972
|
+
return globalThis.localStorage;
|
|
973
|
+
} catch {
|
|
974
|
+
return null;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// src/client/auth-sync.ts
|
|
979
|
+
import {
|
|
980
|
+
buildScopeMap,
|
|
981
|
+
extractScopeValuesFromClaims
|
|
982
|
+
} from "@korajs/core";
|
|
983
|
+
function decodeJwtPayload2(token) {
|
|
984
|
+
const parts = token.split(".");
|
|
985
|
+
if (parts.length !== 3) {
|
|
986
|
+
return null;
|
|
987
|
+
}
|
|
988
|
+
const payloadSegment = parts[1];
|
|
989
|
+
if (payloadSegment === void 0) {
|
|
990
|
+
return null;
|
|
991
|
+
}
|
|
992
|
+
try {
|
|
993
|
+
const base64 = payloadSegment.replace(/-/g, "+").replace(/_/g, "/");
|
|
994
|
+
const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
|
|
995
|
+
const json = typeof atob === "function" ? atob(padded) : Buffer.from(padded, "base64").toString("utf-8");
|
|
996
|
+
const parsed = JSON.parse(json);
|
|
997
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
998
|
+
return null;
|
|
999
|
+
}
|
|
1000
|
+
return parsed;
|
|
1001
|
+
} catch {
|
|
1002
|
+
return null;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
function readDeviceIdFromClaims(claims) {
|
|
1006
|
+
const dev = claims.dev;
|
|
1007
|
+
return typeof dev === "string" && dev.length > 0 ? dev : void 0;
|
|
1008
|
+
}
|
|
1009
|
+
function createKoraAuthSync(options) {
|
|
1010
|
+
const { authClient, schema, scopeFromClaims } = options;
|
|
1011
|
+
const binding = {
|
|
1012
|
+
auth: async () => {
|
|
1013
|
+
const token = await authClient.getAccessToken();
|
|
1014
|
+
return { token: token ?? "" };
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
if (schema) {
|
|
1018
|
+
binding.resolveScopeMap = async () => {
|
|
1019
|
+
const token = await authClient.getAccessToken();
|
|
1020
|
+
if (!token) {
|
|
1021
|
+
return void 0;
|
|
440
1022
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
1023
|
+
const claims = decodeJwtPayload2(token);
|
|
1024
|
+
if (!claims) {
|
|
1025
|
+
return void 0;
|
|
1026
|
+
}
|
|
1027
|
+
const scopeValues = scopeFromClaims ? scopeFromClaims(claims) : extractScopeValuesFromClaims(schema, claims);
|
|
1028
|
+
return buildScopeMap(schema, scopeValues);
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
binding.resolveNodeId = async () => {
|
|
1032
|
+
const token = await authClient.getAccessToken();
|
|
1033
|
+
if (!token) {
|
|
1034
|
+
return void 0;
|
|
446
1035
|
}
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
1036
|
+
const claims = decodeJwtPayload2(token);
|
|
1037
|
+
if (!claims) {
|
|
1038
|
+
return void 0;
|
|
1039
|
+
}
|
|
1040
|
+
return readDeviceIdFromClaims(claims);
|
|
1041
|
+
};
|
|
1042
|
+
if (authClient.onAuthChange) {
|
|
1043
|
+
binding.subscribe = (listener) => authClient.onAuthChange?.(() => listener()) ?? (() => {
|
|
1044
|
+
});
|
|
450
1045
|
}
|
|
451
|
-
|
|
1046
|
+
return binding;
|
|
1047
|
+
}
|
|
452
1048
|
|
|
453
1049
|
// src/client/org-client.ts
|
|
454
|
-
import { KoraError as
|
|
455
|
-
var OrgClientError = class extends
|
|
1050
|
+
import { KoraError as KoraError4 } from "@korajs/core";
|
|
1051
|
+
var OrgClientError = class extends KoraError4 {
|
|
456
1052
|
constructor(message, code, context) {
|
|
457
1053
|
super(message, code, context);
|
|
458
1054
|
this.name = "OrgClientError";
|
|
@@ -711,216 +1307,6 @@ var OrgClient = class {
|
|
|
711
1307
|
}
|
|
712
1308
|
};
|
|
713
1309
|
|
|
714
|
-
// src/device/device-store.ts
|
|
715
|
-
import { KoraError as KoraError3 } from "@korajs/core";
|
|
716
|
-
var DeviceKeyStoreError = class extends KoraError3 {
|
|
717
|
-
constructor(message, context) {
|
|
718
|
-
super(message, "DEVICE_KEY_STORE_ERROR", context);
|
|
719
|
-
this.name = "DeviceKeyStoreError";
|
|
720
|
-
}
|
|
721
|
-
};
|
|
722
|
-
var IDB_DATABASE_NAME = "kora_device_keys";
|
|
723
|
-
var IDB_STORE_NAME = "keypairs";
|
|
724
|
-
var IDB_VERSION = 1;
|
|
725
|
-
var IndexedDBDeviceKeyStore = class {
|
|
726
|
-
dbPromise = null;
|
|
727
|
-
/**
|
|
728
|
-
* Opens (or creates) the IndexedDB database.
|
|
729
|
-
*
|
|
730
|
-
* The database connection is lazily initialized on first use and
|
|
731
|
-
* reused for subsequent operations. If the database does not exist,
|
|
732
|
-
* it is created with the `keypairs` object store.
|
|
733
|
-
*/
|
|
734
|
-
openDatabase() {
|
|
735
|
-
if (this.dbPromise !== null) {
|
|
736
|
-
return this.dbPromise;
|
|
737
|
-
}
|
|
738
|
-
this.dbPromise = new Promise((resolve, reject) => {
|
|
739
|
-
let request;
|
|
740
|
-
try {
|
|
741
|
-
request = globalThis.indexedDB.open(IDB_DATABASE_NAME, IDB_VERSION);
|
|
742
|
-
} catch (cause) {
|
|
743
|
-
this.dbPromise = null;
|
|
744
|
-
reject(
|
|
745
|
-
new DeviceKeyStoreError(
|
|
746
|
-
"Failed to open IndexedDB database for device key storage. IndexedDB may be unavailable or access may be denied.",
|
|
747
|
-
{ cause: cause instanceof Error ? cause.message : String(cause) }
|
|
748
|
-
)
|
|
749
|
-
);
|
|
750
|
-
return;
|
|
751
|
-
}
|
|
752
|
-
request.onupgradeneeded = () => {
|
|
753
|
-
const db = request.result;
|
|
754
|
-
if (!db.objectStoreNames.contains(IDB_STORE_NAME)) {
|
|
755
|
-
db.createObjectStore(IDB_STORE_NAME);
|
|
756
|
-
}
|
|
757
|
-
};
|
|
758
|
-
request.onsuccess = () => {
|
|
759
|
-
resolve(request.result);
|
|
760
|
-
};
|
|
761
|
-
request.onerror = () => {
|
|
762
|
-
this.dbPromise = null;
|
|
763
|
-
reject(
|
|
764
|
-
new DeviceKeyStoreError("Failed to open IndexedDB database for device key storage.", {
|
|
765
|
-
error: request.error?.message
|
|
766
|
-
})
|
|
767
|
-
);
|
|
768
|
-
};
|
|
769
|
-
request.onblocked = () => {
|
|
770
|
-
this.dbPromise = null;
|
|
771
|
-
reject(
|
|
772
|
-
new DeviceKeyStoreError(
|
|
773
|
-
"IndexedDB database open was blocked. Another tab may have an older version of the database open. Close other tabs and try again."
|
|
774
|
-
)
|
|
775
|
-
);
|
|
776
|
-
};
|
|
777
|
-
});
|
|
778
|
-
return this.dbPromise;
|
|
779
|
-
}
|
|
780
|
-
/** @inheritdoc */
|
|
781
|
-
async saveKeyPair(deviceId, keyPair) {
|
|
782
|
-
const db = await this.openDatabase();
|
|
783
|
-
return new Promise((resolve, reject) => {
|
|
784
|
-
try {
|
|
785
|
-
const tx = db.transaction(IDB_STORE_NAME, "readwrite");
|
|
786
|
-
const store = tx.objectStore(IDB_STORE_NAME);
|
|
787
|
-
store.put(keyPair, deviceId);
|
|
788
|
-
tx.oncomplete = () => {
|
|
789
|
-
resolve();
|
|
790
|
-
};
|
|
791
|
-
tx.onerror = () => {
|
|
792
|
-
reject(
|
|
793
|
-
new DeviceKeyStoreError(`Failed to save key pair for device "${deviceId}".`, {
|
|
794
|
-
deviceId,
|
|
795
|
-
error: tx.error?.message
|
|
796
|
-
})
|
|
797
|
-
);
|
|
798
|
-
};
|
|
799
|
-
} catch (cause) {
|
|
800
|
-
reject(
|
|
801
|
-
new DeviceKeyStoreError(`Failed to save key pair for device "${deviceId}".`, {
|
|
802
|
-
deviceId,
|
|
803
|
-
cause: cause instanceof Error ? cause.message : String(cause)
|
|
804
|
-
})
|
|
805
|
-
);
|
|
806
|
-
}
|
|
807
|
-
});
|
|
808
|
-
}
|
|
809
|
-
/** @inheritdoc */
|
|
810
|
-
async loadKeyPair(deviceId) {
|
|
811
|
-
const db = await this.openDatabase();
|
|
812
|
-
return new Promise((resolve, reject) => {
|
|
813
|
-
try {
|
|
814
|
-
const tx = db.transaction(IDB_STORE_NAME, "readonly");
|
|
815
|
-
const store = tx.objectStore(IDB_STORE_NAME);
|
|
816
|
-
const request = store.get(deviceId);
|
|
817
|
-
request.onsuccess = () => {
|
|
818
|
-
const result = request.result;
|
|
819
|
-
resolve(result ?? null);
|
|
820
|
-
};
|
|
821
|
-
request.onerror = () => {
|
|
822
|
-
reject(
|
|
823
|
-
new DeviceKeyStoreError(`Failed to load key pair for device "${deviceId}".`, {
|
|
824
|
-
deviceId,
|
|
825
|
-
error: request.error?.message
|
|
826
|
-
})
|
|
827
|
-
);
|
|
828
|
-
};
|
|
829
|
-
} catch (cause) {
|
|
830
|
-
reject(
|
|
831
|
-
new DeviceKeyStoreError(`Failed to load key pair for device "${deviceId}".`, {
|
|
832
|
-
deviceId,
|
|
833
|
-
cause: cause instanceof Error ? cause.message : String(cause)
|
|
834
|
-
})
|
|
835
|
-
);
|
|
836
|
-
}
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
/** @inheritdoc */
|
|
840
|
-
async deleteKeyPair(deviceId) {
|
|
841
|
-
const db = await this.openDatabase();
|
|
842
|
-
return new Promise((resolve, reject) => {
|
|
843
|
-
try {
|
|
844
|
-
const tx = db.transaction(IDB_STORE_NAME, "readwrite");
|
|
845
|
-
const store = tx.objectStore(IDB_STORE_NAME);
|
|
846
|
-
store.delete(deviceId);
|
|
847
|
-
tx.oncomplete = () => {
|
|
848
|
-
resolve();
|
|
849
|
-
};
|
|
850
|
-
tx.onerror = () => {
|
|
851
|
-
reject(
|
|
852
|
-
new DeviceKeyStoreError(`Failed to delete key pair for device "${deviceId}".`, {
|
|
853
|
-
deviceId,
|
|
854
|
-
error: tx.error?.message
|
|
855
|
-
})
|
|
856
|
-
);
|
|
857
|
-
};
|
|
858
|
-
} catch (cause) {
|
|
859
|
-
reject(
|
|
860
|
-
new DeviceKeyStoreError(`Failed to delete key pair for device "${deviceId}".`, {
|
|
861
|
-
deviceId,
|
|
862
|
-
cause: cause instanceof Error ? cause.message : String(cause)
|
|
863
|
-
})
|
|
864
|
-
);
|
|
865
|
-
}
|
|
866
|
-
});
|
|
867
|
-
}
|
|
868
|
-
/** @inheritdoc */
|
|
869
|
-
async hasKeyPair(deviceId) {
|
|
870
|
-
const db = await this.openDatabase();
|
|
871
|
-
return new Promise((resolve, reject) => {
|
|
872
|
-
try {
|
|
873
|
-
const tx = db.transaction(IDB_STORE_NAME, "readonly");
|
|
874
|
-
const store = tx.objectStore(IDB_STORE_NAME);
|
|
875
|
-
const request = store.count(deviceId);
|
|
876
|
-
request.onsuccess = () => {
|
|
877
|
-
resolve(request.result > 0);
|
|
878
|
-
};
|
|
879
|
-
request.onerror = () => {
|
|
880
|
-
reject(
|
|
881
|
-
new DeviceKeyStoreError(
|
|
882
|
-
`Failed to check if key pair exists for device "${deviceId}".`,
|
|
883
|
-
{ deviceId, error: request.error?.message }
|
|
884
|
-
)
|
|
885
|
-
);
|
|
886
|
-
};
|
|
887
|
-
} catch (cause) {
|
|
888
|
-
reject(
|
|
889
|
-
new DeviceKeyStoreError(`Failed to check if key pair exists for device "${deviceId}".`, {
|
|
890
|
-
deviceId,
|
|
891
|
-
cause: cause instanceof Error ? cause.message : String(cause)
|
|
892
|
-
})
|
|
893
|
-
);
|
|
894
|
-
}
|
|
895
|
-
});
|
|
896
|
-
}
|
|
897
|
-
};
|
|
898
|
-
var InMemoryDeviceKeyStore = class {
|
|
899
|
-
store = /* @__PURE__ */ new Map();
|
|
900
|
-
/** @inheritdoc */
|
|
901
|
-
async saveKeyPair(deviceId, keyPair) {
|
|
902
|
-
this.store.set(deviceId, keyPair);
|
|
903
|
-
}
|
|
904
|
-
/** @inheritdoc */
|
|
905
|
-
async loadKeyPair(deviceId) {
|
|
906
|
-
return this.store.get(deviceId) ?? null;
|
|
907
|
-
}
|
|
908
|
-
/** @inheritdoc */
|
|
909
|
-
async deleteKeyPair(deviceId) {
|
|
910
|
-
this.store.delete(deviceId);
|
|
911
|
-
}
|
|
912
|
-
/** @inheritdoc */
|
|
913
|
-
async hasKeyPair(deviceId) {
|
|
914
|
-
return this.store.has(deviceId);
|
|
915
|
-
}
|
|
916
|
-
};
|
|
917
|
-
function createDeviceKeyStore() {
|
|
918
|
-
if (typeof globalThis.indexedDB !== "undefined") {
|
|
919
|
-
return new IndexedDBDeviceKeyStore();
|
|
920
|
-
}
|
|
921
|
-
return new InMemoryDeviceKeyStore();
|
|
922
|
-
}
|
|
923
|
-
|
|
924
1310
|
// src/tokens/token-store.ts
|
|
925
1311
|
var DEFAULT_STORAGE_KEY = "kora_auth_tokens";
|
|
926
1312
|
var MemoryStorage = class {
|
|
@@ -1044,8 +1430,8 @@ var TokenStore = class {
|
|
|
1044
1430
|
};
|
|
1045
1431
|
|
|
1046
1432
|
// src/tokens/encrypted-token-store.ts
|
|
1047
|
-
import { KoraError as
|
|
1048
|
-
var EncryptedTokenStoreError = class extends
|
|
1433
|
+
import { KoraError as KoraError5 } from "@korajs/core";
|
|
1434
|
+
var EncryptedTokenStoreError = class extends KoraError5 {
|
|
1049
1435
|
constructor(message, context) {
|
|
1050
1436
|
super(message, "ENCRYPTED_TOKEN_STORE_ERROR", context);
|
|
1051
1437
|
this.name = "EncryptedTokenStoreError";
|
|
@@ -1240,8 +1626,8 @@ var EncryptedTokenStore = class {
|
|
|
1240
1626
|
};
|
|
1241
1627
|
|
|
1242
1628
|
// src/encryption/key-derivation.ts
|
|
1243
|
-
import { KoraError as
|
|
1244
|
-
var KeyDerivationError = class extends
|
|
1629
|
+
import { KoraError as KoraError6 } from "@korajs/core";
|
|
1630
|
+
var KeyDerivationError = class extends KoraError6 {
|
|
1245
1631
|
constructor(message, context) {
|
|
1246
1632
|
super(message, "KEY_DERIVATION_ERROR", context);
|
|
1247
1633
|
this.name = "KeyDerivationError";
|
|
@@ -1421,6 +1807,7 @@ var AutoLockManager = class {
|
|
|
1421
1807
|
};
|
|
1422
1808
|
export {
|
|
1423
1809
|
AuthClient,
|
|
1810
|
+
AuthDeviceIdentityError,
|
|
1424
1811
|
AuthError,
|
|
1425
1812
|
AutoLockManager,
|
|
1426
1813
|
CryptoUnavailableError,
|
|
@@ -1440,9 +1827,18 @@ export {
|
|
|
1440
1827
|
PasskeyUnsupportedError,
|
|
1441
1828
|
TokenStore,
|
|
1442
1829
|
authenticateWithPasskey,
|
|
1830
|
+
checkOrgPermission,
|
|
1443
1831
|
computePublicKeyThumbprint,
|
|
1832
|
+
createAuthSession,
|
|
1833
|
+
createAuthTokenStorage,
|
|
1444
1834
|
createDeviceKeyStore,
|
|
1835
|
+
createKoraAuth,
|
|
1836
|
+
createKoraAuthSync,
|
|
1837
|
+
createMemoryAuthTokenStorage,
|
|
1838
|
+
createOrgSession,
|
|
1445
1839
|
createPasskeyCredential,
|
|
1840
|
+
createPersistentDeviceIdentity,
|
|
1841
|
+
createWebStorageAuthTokenStorage,
|
|
1446
1842
|
decryptData,
|
|
1447
1843
|
deriveEncryptionKey,
|
|
1448
1844
|
encryptData,
|