@better-auth/electron 1.6.16 → 1.6.18
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/client.mjs +13 -11
- package/dist/index.mjs +3 -4
- package/dist/preload.d.mts +111 -1
- package/dist/proxy.mjs +1 -1
- package/dist/{version-KncHedVM.mjs → version-DMVLEsxG.mjs} +1 -1
- package/package.json +6 -6
package/dist/client.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { t as PACKAGE_VERSION } from "./version-DMVLEsxG.mjs";
|
|
2
2
|
import { n as isProcessType, r as parseProtocolScheme, t as getChannelPrefixWithDelimiter } from "./utils-DxDKRT6e.mjs";
|
|
3
3
|
import { BetterAuthError } from "@better-auth/core/error";
|
|
4
4
|
import { base64, base64Url } from "@better-auth/utils/base64";
|
|
@@ -579,6 +579,16 @@ const electronClient = (options) => {
|
|
|
579
579
|
const cookieName = `${opts.storagePrefix}.cookie`;
|
|
580
580
|
const localCacheName = `${opts.storagePrefix}.local_cache`;
|
|
581
581
|
const { getDecrypted, setEncrypted } = storageAdapter(opts.storage, new Set([cookieName, localCacheName]));
|
|
582
|
+
const clearSessionCache = () => {
|
|
583
|
+
setEncrypted(cookieName, "{}");
|
|
584
|
+
store?.atoms.session?.set({
|
|
585
|
+
...store.atoms.session.get(),
|
|
586
|
+
data: null,
|
|
587
|
+
error: null,
|
|
588
|
+
isPending: false
|
|
589
|
+
});
|
|
590
|
+
setEncrypted(localCacheName, "{}");
|
|
591
|
+
};
|
|
582
592
|
if ((isDevelopment() || isTest()) && /^(?!\.)(?!.*\.\.)(?!.*\.$)[^.]+\.[^.]+$/.test(scheme)) console.warn("The provided scheme does not follow the reverse domain name notation. For example: `app.example.com` -> `com.example.app`.");
|
|
583
593
|
return {
|
|
584
594
|
id: "electron",
|
|
@@ -598,16 +608,7 @@ const electronClient = (options) => {
|
|
|
598
608
|
"electron-origin": `${scheme}:/`,
|
|
599
609
|
"x-skip-oauth-proxy": "true"
|
|
600
610
|
};
|
|
601
|
-
if (url.endsWith("/sign-out"))
|
|
602
|
-
setEncrypted(cookieName, "{}");
|
|
603
|
-
store?.atoms.session?.set({
|
|
604
|
-
...store.atoms.session.get(),
|
|
605
|
-
data: null,
|
|
606
|
-
error: null,
|
|
607
|
-
isPending: false
|
|
608
|
-
});
|
|
609
|
-
setEncrypted(localCacheName, "{}");
|
|
610
|
-
}
|
|
611
|
+
if (url.endsWith("/sign-out")) clearSessionCache();
|
|
611
612
|
return {
|
|
612
613
|
url,
|
|
613
614
|
options
|
|
@@ -630,6 +631,7 @@ const electronClient = (options) => {
|
|
|
630
631
|
const data = context.data;
|
|
631
632
|
setEncrypted(localCacheName, JSON.stringify(data));
|
|
632
633
|
}
|
|
634
|
+
if (context.request.url.toString().includes("/sign-out")) clearSessionCache();
|
|
633
635
|
},
|
|
634
636
|
onError: async (context) => {
|
|
635
637
|
webContents.getFocusedWebContents()?.send(`${getChannelPrefixWithDelimiter(opts.channelPrefix)}error`, {
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { t as PACKAGE_VERSION } from "./version-DMVLEsxG.mjs";
|
|
2
2
|
import { createAuthMiddleware } from "@better-auth/core/api";
|
|
3
3
|
import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
|
|
4
4
|
import { base64Url } from "@better-auth/utils/base64";
|
|
@@ -57,8 +57,8 @@ const electronToken = (_opts) => createAuthEndpoint("/electron/token", {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
}, async (ctx) => {
|
|
60
|
-
const token = await ctx.context.internalAdapter.
|
|
61
|
-
if (!token
|
|
60
|
+
const token = await ctx.context.internalAdapter.consumeVerificationValue(`electron:${ctx.body.token}`);
|
|
61
|
+
if (!token) throw APIError.from("NOT_FOUND", ELECTRON_ERROR_CODES.INVALID_TOKEN);
|
|
62
62
|
const tokenRecord = safeJSONParse$1(token.value);
|
|
63
63
|
if (!tokenRecord) throw APIError.from("INTERNAL_SERVER_ERROR", ELECTRON_ERROR_CODES.INVALID_TOKEN);
|
|
64
64
|
if (tokenRecord.state !== ctx.body.state) throw APIError.from("BAD_REQUEST", ELECTRON_ERROR_CODES.STATE_MISMATCH);
|
|
@@ -67,7 +67,6 @@ const electronToken = (_opts) => createAuthEndpoint("/electron/token", {
|
|
|
67
67
|
const codeChallenge = Buffer.from(base64Url.decode(tokenRecord.codeChallenge));
|
|
68
68
|
const codeVerifier = Buffer.from(await createHash("SHA-256").digest(ctx.body.code_verifier));
|
|
69
69
|
if (codeChallenge.length !== codeVerifier.length || !timingSafeEqual(codeChallenge, codeVerifier)) throw APIError.from("BAD_REQUEST", ELECTRON_ERROR_CODES.INVALID_CODE_VERIFIER);
|
|
70
|
-
await ctx.context.internalAdapter.deleteVerificationByIdentifier(`electron:${ctx.body.token}`);
|
|
71
70
|
const user = await ctx.context.internalAdapter.findUserById(tokenRecord.userId);
|
|
72
71
|
if (!user) throw APIError.from("INTERNAL_SERVER_ERROR", BASE_ERROR_CODES.USER_NOT_FOUND);
|
|
73
72
|
const session = await ctx.context.internalAdapter.createSession(user.id);
|
package/dist/preload.d.mts
CHANGED
|
@@ -31,6 +31,7 @@ type DBAdapterDebugLogOption = boolean | {
|
|
|
31
31
|
delete?: boolean | undefined;
|
|
32
32
|
deleteMany?: boolean | undefined;
|
|
33
33
|
consumeOne?: boolean | undefined;
|
|
34
|
+
incrementOne?: boolean | undefined;
|
|
34
35
|
count?: boolean | undefined;
|
|
35
36
|
} | {
|
|
36
37
|
/**
|
|
@@ -206,7 +207,7 @@ interface DBAdapterFactoryConfig<Options extends BetterAuthOptions = BetterAuthO
|
|
|
206
207
|
/**
|
|
207
208
|
* The action which was called from the adapter.
|
|
208
209
|
*/
|
|
209
|
-
action: "create" | "update" | "findOne" | "findMany" | "updateMany" | "delete" | "deleteMany" | "consumeOne" | "count";
|
|
210
|
+
action: "create" | "update" | "findOne" | "findMany" | "updateMany" | "delete" | "deleteMany" | "consumeOne" | "incrementOne" | "count";
|
|
210
211
|
/**
|
|
211
212
|
* The model name.
|
|
212
213
|
*/
|
|
@@ -444,6 +445,36 @@ type DBAdapter<Options extends BetterAuthOptions = BetterAuthOptions> = {
|
|
|
444
445
|
model: string;
|
|
445
446
|
where: Where[];
|
|
446
447
|
}) => Promise<T | null>;
|
|
448
|
+
/**
|
|
449
|
+
* Atomically apply signed numeric deltas to a single row matching the where
|
|
450
|
+
* clause. For each entry in `increment`, the operation applies
|
|
451
|
+
* `field = field + delta` in one atomic step; a negative delta decrements.
|
|
452
|
+
*
|
|
453
|
+
* The `where` clause is both the selector AND the guard: comparison
|
|
454
|
+
* operators are honored, so passing `{ field: "remaining", operator: "gt",
|
|
455
|
+
* value: 0 }` only mutates the row while `remaining` is still above zero.
|
|
456
|
+
* When the guard matches no row, the operation makes no change and returns
|
|
457
|
+
* `null`.
|
|
458
|
+
*
|
|
459
|
+
* The optional `set` map assigns absolute values to fields in the same
|
|
460
|
+
* atomic operation, alongside the increments.
|
|
461
|
+
*
|
|
462
|
+
* Returns the updated row, or `null` when the guard matched no row. Under
|
|
463
|
+
* concurrent invocation against the same row, this is the race-safe
|
|
464
|
+
* primitive for guarded counter updates (e.g. decrementing a remaining-uses
|
|
465
|
+
* counter only while it is still positive).
|
|
466
|
+
*
|
|
467
|
+
* Always defined on the factory-wrapped adapter. When the underlying
|
|
468
|
+
* `CustomAdapter` does not implement `incrementOne`, the factory provides a
|
|
469
|
+
* fallback that wraps `findMany + updateMany` in `transaction(...)` and
|
|
470
|
+
* re-applies the where clause as a compare-and-swap guard on the update.
|
|
471
|
+
*/
|
|
472
|
+
incrementOne: <T>(data: {
|
|
473
|
+
model: string;
|
|
474
|
+
where: Where[];
|
|
475
|
+
increment: Record<string, number>;
|
|
476
|
+
set?: Record<string, unknown> | undefined;
|
|
477
|
+
}) => Promise<T | null>;
|
|
447
478
|
/**
|
|
448
479
|
* Execute multiple operations in a transaction.
|
|
449
480
|
* If the adapter doesn't support transactions, operations will be executed sequentially.
|
|
@@ -538,6 +569,25 @@ interface CustomAdapter {
|
|
|
538
569
|
model: string;
|
|
539
570
|
where: CleanedWhere[];
|
|
540
571
|
}) => Promise<T | null>;
|
|
572
|
+
/**
|
|
573
|
+
* Optional native atomic guarded counter mutation. Applies
|
|
574
|
+
* `field = field + delta` for each entry in `increment` (negative deltas
|
|
575
|
+
* decrement), with `where` acting as both selector and guard and `set`
|
|
576
|
+
* assigning absolute values in the same operation. Returns the updated row,
|
|
577
|
+
* or `null` when the guard matched no row.
|
|
578
|
+
*
|
|
579
|
+
* Implementing this natively (e.g. `UPDATE ... SET n = n + $delta WHERE ...
|
|
580
|
+
* RETURNING *`) gives one round trip and the strongest race-safety
|
|
581
|
+
* guarantee. When omitted, the adapter factory provides a transaction-based
|
|
582
|
+
* fallback over `findMany + updateMany`. TODO(increment-one-required):
|
|
583
|
+
* tighten to required in the next minor on `next`.
|
|
584
|
+
*/
|
|
585
|
+
incrementOne?: <T>(data: {
|
|
586
|
+
model: string;
|
|
587
|
+
where: CleanedWhere[];
|
|
588
|
+
increment: Record<string, number>;
|
|
589
|
+
set?: Record<string, unknown> | undefined;
|
|
590
|
+
}) => Promise<T | null>;
|
|
541
591
|
count: ({
|
|
542
592
|
model,
|
|
543
593
|
where
|
|
@@ -4938,6 +4988,22 @@ interface InternalAdapter<_Options extends BetterAuthOptions = BetterAuthOptions
|
|
|
4938
4988
|
* pair at single-use credential consumption sites.
|
|
4939
4989
|
*/
|
|
4940
4990
|
consumeVerificationValue(identifier: string): Promise<Verification | null>;
|
|
4991
|
+
/**
|
|
4992
|
+
* First-writer-wins create keyed by a deterministic primary key derived from
|
|
4993
|
+
* `identifier`. Returns `true` when this caller created the row and `false`
|
|
4994
|
+
* when a row for the same identifier already existed.
|
|
4995
|
+
*
|
|
4996
|
+
* The dual of `consumeVerificationValue`: reserve races to create a marker
|
|
4997
|
+
* exactly once, where consume races to delete one exactly once. Use it for
|
|
4998
|
+
* replay tombstones (a SAML assertion id, a JWT `jti`) where the first caller
|
|
4999
|
+
* wins. The database path is atomic via the primary key; the
|
|
5000
|
+
* secondary-storage-only path is best-effort under concurrency.
|
|
5001
|
+
*/
|
|
5002
|
+
reserveVerificationValue(data: {
|
|
5003
|
+
identifier: string;
|
|
5004
|
+
value: string;
|
|
5005
|
+
expiresAt: Date;
|
|
5006
|
+
}): Promise<boolean>;
|
|
4941
5007
|
updateVerificationByIdentifier(identifier: string, data: Partial<Verification>): Promise<Verification>;
|
|
4942
5008
|
refreshUserSessions(user: User): Promise<void>;
|
|
4943
5009
|
}
|
|
@@ -5424,6 +5490,35 @@ type BaseURLConfig = string | DynamicBaseURLConfig;
|
|
|
5424
5490
|
interface BetterAuthRateLimitStorage {
|
|
5425
5491
|
get: (key: string) => Promise<RateLimit | null | undefined>;
|
|
5426
5492
|
set: (key: string, value: RateLimit, update?: boolean | undefined) => Promise<void>;
|
|
5493
|
+
/**
|
|
5494
|
+
* Atomically records one request against `key` within the `window`
|
|
5495
|
+
* (in seconds) and reports whether it is allowed.
|
|
5496
|
+
*
|
|
5497
|
+
* When `allowed` is true the request was counted within the active window;
|
|
5498
|
+
* when `allowed` is false the limit was already reached and `retryAfter` is
|
|
5499
|
+
* the number of seconds until the window frees up. Whether the window slides
|
|
5500
|
+
* or is fixed depends on the backing storage: the database backend resets
|
|
5501
|
+
* once the window elapses, while secondary storage uses a fixed time-to-live
|
|
5502
|
+
* set when the window first opens.
|
|
5503
|
+
*
|
|
5504
|
+
* Performing the check and the increment in a single step closes the
|
|
5505
|
+
* concurrent-bypass gap of the separate `get`/`set` path: N simultaneous
|
|
5506
|
+
* requests can no longer all pass a stale read before any increment lands.
|
|
5507
|
+
*
|
|
5508
|
+
* Optional for backwards compatibility. A storage without it falls back to
|
|
5509
|
+
* the legacy non-atomic `get`/`set` path, which is best-effort under
|
|
5510
|
+
* concurrency.
|
|
5511
|
+
*
|
|
5512
|
+
* TODO(rate-limit-consume-required): make this the sole required member on
|
|
5513
|
+
* `next`, dropping `get`/`set` and the non-atomic fallback.
|
|
5514
|
+
*/
|
|
5515
|
+
consume?: (key: string, rule: {
|
|
5516
|
+
window: number;
|
|
5517
|
+
max: number;
|
|
5518
|
+
}) => Promise<{
|
|
5519
|
+
allowed: boolean;
|
|
5520
|
+
retryAfter: number | null;
|
|
5521
|
+
}>;
|
|
5427
5522
|
}
|
|
5428
5523
|
type BetterAuthRateLimitRule = {
|
|
5429
5524
|
/**
|
|
@@ -6877,6 +6972,21 @@ interface SecondaryStorage {
|
|
|
6877
6972
|
* security-sensitive consume paths.
|
|
6878
6973
|
*/
|
|
6879
6974
|
getAndDelete?: (key: string) => Awaitable<unknown>;
|
|
6975
|
+
/**
|
|
6976
|
+
* Atomically increment the counter at `key` by one, returning the
|
|
6977
|
+
* post-increment value.
|
|
6978
|
+
*
|
|
6979
|
+
* When the key is absent, it is created with a value of `1` and the given
|
|
6980
|
+
* `ttl` (in SECONDS). The TTL is applied only on creation; later increments
|
|
6981
|
+
* never extend it, so the counter expires a fixed window after it was first
|
|
6982
|
+
* created.
|
|
6983
|
+
*
|
|
6984
|
+
* This is optional for backwards compatibility with existing secondary
|
|
6985
|
+
* storage implementations. TODO(secondary-storage-increment-required): make
|
|
6986
|
+
* this required for secondary-storage-backed rate limiting in the next minor
|
|
6987
|
+
* on `next`.
|
|
6988
|
+
*/
|
|
6989
|
+
increment?: (key: string, ttl: number) => Awaitable<number>;
|
|
6880
6990
|
set: (
|
|
6881
6991
|
/**
|
|
6882
6992
|
* Key to store
|
package/dist/proxy.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { t as PACKAGE_VERSION } from "./version-DMVLEsxG.mjs";
|
|
2
2
|
import { r as parseProtocolScheme } from "./utils-DxDKRT6e.mjs";
|
|
3
3
|
import { parseCookies } from "better-auth/cookies";
|
|
4
4
|
//#region src/proxy.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/electron",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.18",
|
|
4
4
|
"description": "Better Auth integration for Electron applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -80,17 +80,17 @@
|
|
|
80
80
|
"better-sqlite3": "^12.6.2",
|
|
81
81
|
"electron": "^41.2.2",
|
|
82
82
|
"tsdown": "0.21.1",
|
|
83
|
-
"@better-auth/core": "1.6.
|
|
84
|
-
"better-auth": "1.6.
|
|
83
|
+
"@better-auth/core": "1.6.18",
|
|
84
|
+
"better-auth": "1.6.18"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
87
|
"@better-auth/utils": "0.4.1",
|
|
88
|
-
"@better-fetch/fetch": "1.
|
|
88
|
+
"@better-fetch/fetch": "1.3.0",
|
|
89
89
|
"better-call": "1.3.6",
|
|
90
90
|
"conf": "^15.0.2",
|
|
91
91
|
"electron": ">=36.0.0",
|
|
92
|
-
"@better-auth/core": "^1.6.
|
|
93
|
-
"better-auth": "^1.6.
|
|
92
|
+
"@better-auth/core": "^1.6.18",
|
|
93
|
+
"better-auth": "^1.6.18"
|
|
94
94
|
},
|
|
95
95
|
"peerDependenciesMeta": {
|
|
96
96
|
"electron": {
|