@better-auth/electron 1.5.0-beta.13 → 1.5.0-beta.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/{authenticate-CWAVJ4W8.d.mts → authenticate-DifQcrCY.d.mts} +19 -43
- package/dist/browser-jzFaG_9T.d.mts +57 -0
- package/dist/client.d.mts +172 -3
- package/dist/client.mjs +119 -172
- package/dist/index.d.mts +132 -38
- package/dist/index.mjs +118 -28
- package/dist/options-DTTv7_dq.d.mts +38 -0
- package/dist/preload.d.mts +6832 -0
- package/dist/preload.mjs +254 -0
- package/dist/proxy.d.mts +6 -1
- package/dist/proxy.mjs +4 -2
- package/dist/storage.d.mts +3 -2
- package/dist/{utils-C3fLmbAT.mjs → utils-DecLU66o.mjs} +4 -1
- package/package.json +17 -7
- package/dist/client-BBp9yCmE.d.mts +0 -224
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { n as ElectronSharedOptions } from "./options-DTTv7_dq.mjs";
|
|
1
2
|
import * as z from "zod";
|
|
2
3
|
import { BetterFetch } from "@better-fetch/fetch";
|
|
3
4
|
|
|
@@ -6,13 +7,7 @@ interface Storage {
|
|
|
6
7
|
getItem: (name: string) => unknown | null;
|
|
7
8
|
setItem: (name: string, value: unknown) => void;
|
|
8
9
|
}
|
|
9
|
-
interface
|
|
10
|
-
/**
|
|
11
|
-
* The URL to redirect to for authentication.
|
|
12
|
-
*
|
|
13
|
-
* @example "http://localhost:3000/sign-in"
|
|
14
|
-
*/
|
|
15
|
-
signInURL: string | URL;
|
|
10
|
+
interface ElectronSharedClientOptions extends ElectronSharedOptions {
|
|
16
11
|
/**
|
|
17
12
|
* The protocol scheme to use for deep linking in Electron.
|
|
18
13
|
*
|
|
@@ -23,7 +18,6 @@ interface ElectronClientOptions {
|
|
|
23
18
|
*/
|
|
24
19
|
protocol: string | {
|
|
25
20
|
scheme: string;
|
|
26
|
-
privileges?: Electron.Privileges | undefined;
|
|
27
21
|
};
|
|
28
22
|
/**
|
|
29
23
|
* The callback path to use for authentication redirects.
|
|
@@ -31,14 +25,27 @@ interface ElectronClientOptions {
|
|
|
31
25
|
* @default "/auth/callback"
|
|
32
26
|
*/
|
|
33
27
|
callbackPath?: string;
|
|
28
|
+
}
|
|
29
|
+
interface ElectronClientOptions extends ElectronSharedClientOptions {
|
|
34
30
|
/**
|
|
35
|
-
*
|
|
31
|
+
* The URL to redirect to for authentication.
|
|
32
|
+
*
|
|
33
|
+
* @example "http://localhost:3000/sign-in"
|
|
34
|
+
*/
|
|
35
|
+
signInURL: string | URL;
|
|
36
|
+
protocol: string | {
|
|
37
|
+
scheme: string;
|
|
38
|
+
privileges?: Electron.Privileges | undefined;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* An instance of a storage solution (e.g., `conf`)
|
|
36
42
|
* to store session and cookie data.
|
|
37
43
|
*
|
|
38
44
|
* @example
|
|
39
45
|
* ```ts
|
|
46
|
+
* import { storage } from "@better-auth/electron/storage";
|
|
40
47
|
* electronClient({
|
|
41
|
-
* storage:
|
|
48
|
+
* storage: storage(),
|
|
42
49
|
* });
|
|
43
50
|
* ```
|
|
44
51
|
*/
|
|
@@ -66,12 +73,6 @@ interface ElectronClientOptions {
|
|
|
66
73
|
* @default "better-auth"
|
|
67
74
|
*/
|
|
68
75
|
channelPrefix?: string | undefined;
|
|
69
|
-
/**
|
|
70
|
-
* Client ID to use for identifying the Electron client during authorization.
|
|
71
|
-
*
|
|
72
|
-
* @default "electron"
|
|
73
|
-
*/
|
|
74
|
-
clientID?: string | undefined;
|
|
75
76
|
/**
|
|
76
77
|
* Whether to disable caching the session data locally.
|
|
77
78
|
*
|
|
@@ -79,32 +80,7 @@ interface ElectronClientOptions {
|
|
|
79
80
|
*/
|
|
80
81
|
disableCache?: boolean | undefined;
|
|
81
82
|
}
|
|
82
|
-
interface ElectronProxyClientOptions {
|
|
83
|
-
/**
|
|
84
|
-
* The protocol scheme to use for deep linking in Electron.
|
|
85
|
-
*
|
|
86
|
-
* Should follow the reverse domain name notation to ensure uniqueness.
|
|
87
|
-
*
|
|
88
|
-
* Note that this must match the protocol scheme registered in the server plugin.
|
|
89
|
-
*
|
|
90
|
-
* @see {@link https://datatracker.ietf.org/doc/html/rfc8252#section-7.1}
|
|
91
|
-
* @example "com.example.app"
|
|
92
|
-
*/
|
|
93
|
-
protocol: string | {
|
|
94
|
-
scheme: string;
|
|
95
|
-
};
|
|
96
|
-
/**
|
|
97
|
-
* The callback path to use for authentication redirects.
|
|
98
|
-
*
|
|
99
|
-
* @default "/auth/callback"
|
|
100
|
-
*/
|
|
101
|
-
callbackPath?: string;
|
|
102
|
-
/**
|
|
103
|
-
* Client ID to use for identifying the Electron client during authorization.
|
|
104
|
-
*
|
|
105
|
-
* @default "electron"
|
|
106
|
-
*/
|
|
107
|
-
clientID?: string | undefined;
|
|
83
|
+
interface ElectronProxyClientOptions extends ElectronSharedClientOptions {
|
|
108
84
|
/**
|
|
109
85
|
* The prefix to use for cookies set by the plugin.
|
|
110
86
|
*
|
|
@@ -126,4 +102,4 @@ declare const requestAuthOptionsSchema: z.ZodObject<{
|
|
|
126
102
|
}, z.core.$strip>;
|
|
127
103
|
type ElectronRequestAuthOptions = z.infer<typeof requestAuthOptionsSchema>;
|
|
128
104
|
//#endregion
|
|
129
|
-
export { Storage as i, ElectronClientOptions as n, ElectronProxyClientOptions as r, ElectronRequestAuthOptions as t };
|
|
105
|
+
export { Storage as a, ElectronSharedClientOptions as i, ElectronClientOptions as n, ElectronProxyClientOptions as r, ElectronRequestAuthOptions as t };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { n as ElectronClientOptions, t as ElectronRequestAuthOptions } from "./authenticate-DifQcrCY.mjs";
|
|
2
|
+
import { BetterFetch, BetterFetchError } from "@better-fetch/fetch";
|
|
3
|
+
import electron from "electron";
|
|
4
|
+
import { User } from "@better-auth/core/db";
|
|
5
|
+
|
|
6
|
+
//#region src/preload.d.ts
|
|
7
|
+
type ExposedBridges = ReturnType<typeof exposeBridges>["$InferBridges"];
|
|
8
|
+
/**
|
|
9
|
+
* Exposes IPC bridges to the renderer process.
|
|
10
|
+
*/
|
|
11
|
+
declare function exposeBridges(opts: SetupRendererConfig): {
|
|
12
|
+
$InferBridges: {
|
|
13
|
+
getUser: () => Promise<{
|
|
14
|
+
id: string;
|
|
15
|
+
createdAt: Date;
|
|
16
|
+
updatedAt: Date;
|
|
17
|
+
email: string;
|
|
18
|
+
emailVerified: boolean;
|
|
19
|
+
name: string;
|
|
20
|
+
image?: string | null | undefined;
|
|
21
|
+
} & Record<string, any>>;
|
|
22
|
+
requestAuth: (options?: ElectronRequestAuthOptions) => Promise<void>;
|
|
23
|
+
signOut: () => Promise<void>;
|
|
24
|
+
onAuthenticated: (callback: (user: User & Record<string, any>) => unknown) => () => void;
|
|
25
|
+
onUserUpdated: (callback: (user: (User & Record<string, any>) | null) => unknown) => () => void;
|
|
26
|
+
onAuthError: (callback: (context: BetterFetchError & {
|
|
27
|
+
path: string;
|
|
28
|
+
}) => unknown) => () => void;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
interface SetupRendererConfig {
|
|
32
|
+
channelPrefix?: ElectronClientOptions["channelPrefix"] | undefined;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/browser.d.ts
|
|
36
|
+
type SetupMainConfig = {
|
|
37
|
+
getWindow?: () => electron.BrowserWindow | null | undefined;
|
|
38
|
+
csp?: boolean | undefined;
|
|
39
|
+
bridges?: boolean | undefined;
|
|
40
|
+
scheme?: boolean | undefined;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Handles the deep link URL for authentication.
|
|
44
|
+
*/
|
|
45
|
+
declare function handleDeepLink({
|
|
46
|
+
$fetch,
|
|
47
|
+
options,
|
|
48
|
+
url,
|
|
49
|
+
getWindow
|
|
50
|
+
}: {
|
|
51
|
+
$fetch: BetterFetch;
|
|
52
|
+
options: ElectronClientOptions;
|
|
53
|
+
url: string;
|
|
54
|
+
getWindow?: SetupMainConfig["getWindow"] | undefined;
|
|
55
|
+
}): Promise<void>;
|
|
56
|
+
//#endregion
|
|
57
|
+
export { ExposedBridges as n, handleDeepLink as t };
|
package/dist/client.d.mts
CHANGED
|
@@ -1,3 +1,172 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as
|
|
3
|
-
|
|
1
|
+
import { n as ElectronSharedOptions } from "./options-DTTv7_dq.mjs";
|
|
2
|
+
import { a as Storage, i as ElectronSharedClientOptions, n as ElectronClientOptions, r as ElectronProxyClientOptions, t as ElectronRequestAuthOptions } from "./authenticate-DifQcrCY.mjs";
|
|
3
|
+
import { n as ExposedBridges, t as handleDeepLink } from "./browser-jzFaG_9T.mjs";
|
|
4
|
+
import * as better_auth0 from "better-auth";
|
|
5
|
+
import { ClientStore } from "better-auth";
|
|
6
|
+
import * as _better_fetch_fetch0 from "@better-fetch/fetch";
|
|
7
|
+
import electron from "electron";
|
|
8
|
+
|
|
9
|
+
//#region src/client.d.ts
|
|
10
|
+
declare const electronClient: (options: ElectronClientOptions) => {
|
|
11
|
+
id: "electron";
|
|
12
|
+
fetchPlugins: {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
init(url: string, options: ({
|
|
16
|
+
cache?: RequestCache | undefined;
|
|
17
|
+
credentials?: RequestCredentials | undefined;
|
|
18
|
+
headers?: (HeadersInit & (HeadersInit | {
|
|
19
|
+
accept: "application/json" | "text/plain" | "application/octet-stream";
|
|
20
|
+
"content-type": "application/json" | "text/plain" | "application/x-www-form-urlencoded" | "multipart/form-data" | "application/octet-stream";
|
|
21
|
+
authorization: "Bearer" | "Basic";
|
|
22
|
+
})) | undefined;
|
|
23
|
+
integrity?: string | undefined;
|
|
24
|
+
keepalive?: boolean | undefined;
|
|
25
|
+
method?: string | undefined;
|
|
26
|
+
mode?: RequestMode | undefined;
|
|
27
|
+
priority?: RequestPriority | undefined;
|
|
28
|
+
redirect?: RequestRedirect | undefined;
|
|
29
|
+
referrer?: string | undefined;
|
|
30
|
+
referrerPolicy?: ReferrerPolicy | undefined;
|
|
31
|
+
signal?: (AbortSignal | null) | undefined;
|
|
32
|
+
window?: null | undefined;
|
|
33
|
+
onRequest?: (<T extends Record<string, any>>(context: _better_fetch_fetch0.RequestContext<T>) => Promise<_better_fetch_fetch0.RequestContext | void> | _better_fetch_fetch0.RequestContext | void) | undefined;
|
|
34
|
+
onResponse?: ((context: _better_fetch_fetch0.ResponseContext) => Promise<Response | void | _better_fetch_fetch0.ResponseContext> | Response | _better_fetch_fetch0.ResponseContext | void) | undefined;
|
|
35
|
+
onSuccess?: ((context: _better_fetch_fetch0.SuccessContext<any>) => Promise<void> | void) | undefined;
|
|
36
|
+
onError?: ((context: _better_fetch_fetch0.ErrorContext) => Promise<void> | void) | undefined;
|
|
37
|
+
onRetry?: ((response: _better_fetch_fetch0.ResponseContext) => Promise<void> | void) | undefined;
|
|
38
|
+
hookOptions?: {
|
|
39
|
+
cloneResponse?: boolean;
|
|
40
|
+
} | undefined;
|
|
41
|
+
timeout?: number | undefined;
|
|
42
|
+
customFetchImpl?: _better_fetch_fetch0.FetchEsque | undefined;
|
|
43
|
+
plugins?: _better_fetch_fetch0.BetterFetchPlugin[] | undefined;
|
|
44
|
+
baseURL?: string | undefined;
|
|
45
|
+
throw?: boolean | undefined;
|
|
46
|
+
auth?: ({
|
|
47
|
+
type: "Bearer";
|
|
48
|
+
token: string | Promise<string | undefined> | (() => string | Promise<string | undefined> | undefined) | undefined;
|
|
49
|
+
} | {
|
|
50
|
+
type: "Basic";
|
|
51
|
+
username: string | (() => string | undefined) | undefined;
|
|
52
|
+
password: string | (() => string | undefined) | undefined;
|
|
53
|
+
} | {
|
|
54
|
+
type: "Custom";
|
|
55
|
+
prefix: string | (() => string | undefined) | undefined;
|
|
56
|
+
value: string | (() => string | undefined) | undefined;
|
|
57
|
+
}) | undefined;
|
|
58
|
+
body?: any;
|
|
59
|
+
query?: any;
|
|
60
|
+
params?: any;
|
|
61
|
+
duplex?: "full" | "half" | undefined;
|
|
62
|
+
jsonParser?: ((text: string) => Promise<any> | any) | undefined;
|
|
63
|
+
retry?: _better_fetch_fetch0.RetryOptions | undefined;
|
|
64
|
+
retryAttempt?: number | undefined;
|
|
65
|
+
output?: (_better_fetch_fetch0.StandardSchemaV1 | typeof Blob | typeof File) | undefined;
|
|
66
|
+
errorSchema?: _better_fetch_fetch0.StandardSchemaV1 | undefined;
|
|
67
|
+
disableValidation?: boolean | undefined;
|
|
68
|
+
} & Record<string, any>) | undefined): Promise<{
|
|
69
|
+
url: string;
|
|
70
|
+
options: {
|
|
71
|
+
cache?: RequestCache | undefined;
|
|
72
|
+
credentials?: RequestCredentials | undefined;
|
|
73
|
+
headers?: (HeadersInit & (HeadersInit | {
|
|
74
|
+
accept: "application/json" | "text/plain" | "application/octet-stream";
|
|
75
|
+
"content-type": "application/json" | "text/plain" | "application/x-www-form-urlencoded" | "multipart/form-data" | "application/octet-stream";
|
|
76
|
+
authorization: "Bearer" | "Basic";
|
|
77
|
+
})) | undefined;
|
|
78
|
+
integrity?: string | undefined;
|
|
79
|
+
keepalive?: boolean | undefined;
|
|
80
|
+
method?: string | undefined;
|
|
81
|
+
mode?: RequestMode | undefined;
|
|
82
|
+
priority?: RequestPriority | undefined;
|
|
83
|
+
redirect?: RequestRedirect | undefined;
|
|
84
|
+
referrer?: string | undefined;
|
|
85
|
+
referrerPolicy?: ReferrerPolicy | undefined;
|
|
86
|
+
signal?: (AbortSignal | null) | undefined;
|
|
87
|
+
window?: null | undefined;
|
|
88
|
+
onRequest?: (<T extends Record<string, any>>(context: _better_fetch_fetch0.RequestContext<T>) => Promise<_better_fetch_fetch0.RequestContext | void> | _better_fetch_fetch0.RequestContext | void) | undefined;
|
|
89
|
+
onResponse?: ((context: _better_fetch_fetch0.ResponseContext) => Promise<Response | void | _better_fetch_fetch0.ResponseContext> | Response | _better_fetch_fetch0.ResponseContext | void) | undefined;
|
|
90
|
+
onSuccess?: ((context: _better_fetch_fetch0.SuccessContext<any>) => Promise<void> | void) | undefined;
|
|
91
|
+
onError?: ((context: _better_fetch_fetch0.ErrorContext) => Promise<void> | void) | undefined;
|
|
92
|
+
onRetry?: ((response: _better_fetch_fetch0.ResponseContext) => Promise<void> | void) | undefined;
|
|
93
|
+
hookOptions?: {
|
|
94
|
+
cloneResponse?: boolean;
|
|
95
|
+
} | undefined;
|
|
96
|
+
timeout?: number | undefined;
|
|
97
|
+
customFetchImpl?: _better_fetch_fetch0.FetchEsque | undefined;
|
|
98
|
+
plugins?: _better_fetch_fetch0.BetterFetchPlugin[] | undefined;
|
|
99
|
+
baseURL?: string | undefined;
|
|
100
|
+
throw?: boolean | undefined;
|
|
101
|
+
auth?: ({
|
|
102
|
+
type: "Bearer";
|
|
103
|
+
token: string | Promise<string | undefined> | (() => string | Promise<string | undefined> | undefined) | undefined;
|
|
104
|
+
} | {
|
|
105
|
+
type: "Basic";
|
|
106
|
+
username: string | (() => string | undefined) | undefined;
|
|
107
|
+
password: string | (() => string | undefined) | undefined;
|
|
108
|
+
} | {
|
|
109
|
+
type: "Custom";
|
|
110
|
+
prefix: string | (() => string | undefined) | undefined;
|
|
111
|
+
value: string | (() => string | undefined) | undefined;
|
|
112
|
+
}) | undefined;
|
|
113
|
+
body?: any;
|
|
114
|
+
query?: any;
|
|
115
|
+
params?: any;
|
|
116
|
+
duplex?: "full" | "half" | undefined;
|
|
117
|
+
jsonParser?: ((text: string) => Promise<any> | any) | undefined;
|
|
118
|
+
retry?: _better_fetch_fetch0.RetryOptions | undefined;
|
|
119
|
+
retryAttempt?: number | undefined;
|
|
120
|
+
output?: (_better_fetch_fetch0.StandardSchemaV1 | typeof Blob | typeof File) | undefined;
|
|
121
|
+
errorSchema?: _better_fetch_fetch0.StandardSchemaV1 | undefined;
|
|
122
|
+
disableValidation?: boolean | undefined;
|
|
123
|
+
} & Record<string, any>;
|
|
124
|
+
}>;
|
|
125
|
+
hooks: {
|
|
126
|
+
onSuccess: (context: _better_fetch_fetch0.SuccessContext<any>) => Promise<void>;
|
|
127
|
+
onError: (context: _better_fetch_fetch0.ErrorContext) => Promise<void>;
|
|
128
|
+
};
|
|
129
|
+
}[];
|
|
130
|
+
getActions: ($fetch: _better_fetch_fetch0.BetterFetch, $store: ClientStore, clientOptions: better_auth0.BetterAuthClientOptions | undefined) => {
|
|
131
|
+
/**
|
|
132
|
+
* Gets the stored cookie.
|
|
133
|
+
*
|
|
134
|
+
* You can use this to get the cookie stored in
|
|
135
|
+
* the device and use it in your fetch requests.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```ts
|
|
139
|
+
* const cookie = client.getCookie();
|
|
140
|
+
* await fetch("https://api.example.com", {
|
|
141
|
+
* headers: {
|
|
142
|
+
* cookie,
|
|
143
|
+
* },
|
|
144
|
+
* });
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
getCookie: () => string;
|
|
148
|
+
/**
|
|
149
|
+
* Initiates the authentication process.
|
|
150
|
+
* Opens the system's default browser for user authentication.
|
|
151
|
+
*/
|
|
152
|
+
requestAuth: (options?: ElectronRequestAuthOptions | undefined) => Promise<void>;
|
|
153
|
+
/**
|
|
154
|
+
* Sets up the main process.
|
|
155
|
+
*
|
|
156
|
+
* - Registers custom protocol scheme.
|
|
157
|
+
* - Registers IPC bridge handlers.
|
|
158
|
+
* - Handles content security policy if needed.
|
|
159
|
+
*/
|
|
160
|
+
setupMain: (cfg?: {
|
|
161
|
+
csp?: boolean | undefined;
|
|
162
|
+
bridges?: boolean | undefined;
|
|
163
|
+
scheme?: boolean | undefined;
|
|
164
|
+
getWindow?: () => electron.BrowserWindow | null | undefined;
|
|
165
|
+
}) => void;
|
|
166
|
+
$Infer: {
|
|
167
|
+
Bridges: ExposedBridges;
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
//#endregion
|
|
172
|
+
export { ElectronClientOptions, ElectronProxyClientOptions, ElectronRequestAuthOptions, ElectronSharedClientOptions, ElectronSharedOptions, Storage, electronClient, handleDeepLink };
|
package/dist/client.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as parseProtocolScheme, t as
|
|
1
|
+
import { n as isProcessType, r as parseProtocolScheme, t as getChannelPrefixWithDelimiter } from "./utils-DecLU66o.mjs";
|
|
2
2
|
import { BetterAuthError } from "@better-auth/core/error";
|
|
3
3
|
import { APIError as APIError$1, getBaseURL, isDevelopment, isTest } from "better-auth";
|
|
4
4
|
import { generateRandomString } from "better-auth/crypto";
|
|
@@ -11,84 +11,6 @@ import { parseSetCookieHeader } from "better-auth/cookies";
|
|
|
11
11
|
import electron, { shell } from "electron";
|
|
12
12
|
import { resolve } from "node:path";
|
|
13
13
|
|
|
14
|
-
//#region src/bridges.ts
|
|
15
|
-
const { ipcRenderer, ipcMain, contextBridge, webContents: webContents$1 } = electron;
|
|
16
|
-
function getChannelPrefixWithDelimiter(ns = "better-auth") {
|
|
17
|
-
return ns.length > 0 ? ns + ":" : ns;
|
|
18
|
-
}
|
|
19
|
-
function listenerFactory(channel, listener) {
|
|
20
|
-
ipcRenderer.on(channel, listener);
|
|
21
|
-
return () => {
|
|
22
|
-
ipcRenderer.off(channel, listener);
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Exposes IPC bridges to the renderer process.
|
|
27
|
-
*/
|
|
28
|
-
function exposeBridges(opts) {
|
|
29
|
-
if (!process.contextIsolated) throw new BetterAuthError("Context isolation must be enabled to use IPC bridges securely.");
|
|
30
|
-
const prefix = getChannelPrefixWithDelimiter(opts.channelPrefix);
|
|
31
|
-
const bridges = {
|
|
32
|
-
getUser: async () => {
|
|
33
|
-
return await ipcRenderer.invoke(`${prefix}getUser`);
|
|
34
|
-
},
|
|
35
|
-
requestAuth: async (options) => {
|
|
36
|
-
await ipcRenderer.invoke(`${prefix}requestAuth`, options);
|
|
37
|
-
},
|
|
38
|
-
signOut: async () => {
|
|
39
|
-
await ipcRenderer.invoke(`${prefix}signOut`);
|
|
40
|
-
},
|
|
41
|
-
onAuthenticated: (callback) => {
|
|
42
|
-
return listenerFactory(`${prefix}authenticated`, async (_evt, user) => {
|
|
43
|
-
await callback(user);
|
|
44
|
-
});
|
|
45
|
-
},
|
|
46
|
-
onUserUpdated: (callback) => {
|
|
47
|
-
return listenerFactory(`${prefix}user-updated`, async (_evt, user) => {
|
|
48
|
-
await callback(user);
|
|
49
|
-
});
|
|
50
|
-
},
|
|
51
|
-
onAuthError: (callback) => {
|
|
52
|
-
return listenerFactory(`${prefix}error`, async (_evt, context) => {
|
|
53
|
-
await callback(context);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
for (const [key, value] of Object.entries(bridges)) contextBridge.exposeInMainWorld(key, value);
|
|
58
|
-
return {};
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Sets up IPC bridges in the main process.
|
|
62
|
-
*/
|
|
63
|
-
function setupBridges(ctx, opts, clientOptions) {
|
|
64
|
-
const prefix = getChannelPrefixWithDelimiter(opts.channelPrefix);
|
|
65
|
-
ctx.$store?.atoms.session?.subscribe((state) => {
|
|
66
|
-
if (state.isPending === true) return;
|
|
67
|
-
webContents$1.getFocusedWebContents()?.send(`${prefix}user-updated`, state?.data?.user ?? null);
|
|
68
|
-
});
|
|
69
|
-
ipcMain.handle(`${prefix}getUser`, async () => {
|
|
70
|
-
return (await ctx.$fetch("/get-session", {
|
|
71
|
-
method: "GET",
|
|
72
|
-
headers: {
|
|
73
|
-
cookie: ctx.getCookie(),
|
|
74
|
-
"content-type": "application/json"
|
|
75
|
-
}
|
|
76
|
-
})).data?.user ?? null;
|
|
77
|
-
});
|
|
78
|
-
ipcMain.handle(`${prefix}requestAuth`, (_evt, options) => requestAuth(clientOptions, opts, options));
|
|
79
|
-
ipcMain.handle(`${prefix}signOut`, async () => {
|
|
80
|
-
await ctx.$fetch("/sign-out", {
|
|
81
|
-
method: "POST",
|
|
82
|
-
body: "{}",
|
|
83
|
-
headers: {
|
|
84
|
-
cookie: ctx.getCookie(),
|
|
85
|
-
"content-type": "application/json"
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
//#endregion
|
|
92
14
|
//#region src/authenticate.ts
|
|
93
15
|
const kCodeVerifier = Symbol.for("better-auth:code_verifier");
|
|
94
16
|
const kState = Symbol.for("better-auth:state");
|
|
@@ -155,104 +77,14 @@ async function authenticate($fetch, options, body, getWindow) {
|
|
|
155
77
|
}
|
|
156
78
|
|
|
157
79
|
//#endregion
|
|
158
|
-
//#region src/
|
|
159
|
-
|
|
160
|
-
const parsed = parseSetCookieHeader(header);
|
|
161
|
-
let toSetCookie = {};
|
|
162
|
-
parsed.forEach((cookie, key) => {
|
|
163
|
-
const expiresAt = cookie["expires"];
|
|
164
|
-
const maxAge = cookie["max-age"];
|
|
165
|
-
const expires = maxAge ? new Date(Date.now() + Number(maxAge) * 1e3) : expiresAt ? new Date(String(expiresAt)) : null;
|
|
166
|
-
toSetCookie[key] = {
|
|
167
|
-
value: cookie["value"],
|
|
168
|
-
expires: expires ? expires.toISOString() : null
|
|
169
|
-
};
|
|
170
|
-
});
|
|
171
|
-
if (prevCookie) try {
|
|
172
|
-
toSetCookie = {
|
|
173
|
-
...JSON.parse(prevCookie),
|
|
174
|
-
...toSetCookie
|
|
175
|
-
};
|
|
176
|
-
} catch {}
|
|
177
|
-
return JSON.stringify(toSetCookie);
|
|
178
|
-
}
|
|
179
|
-
function getCookie(cookie) {
|
|
180
|
-
let parsed = {};
|
|
181
|
-
try {
|
|
182
|
-
parsed = JSON.parse(cookie);
|
|
183
|
-
} catch (_e) {}
|
|
184
|
-
return Object.entries(parsed).reduce((acc, [key, value]) => {
|
|
185
|
-
if (value.expires && new Date(value.expires) < /* @__PURE__ */ new Date()) return acc;
|
|
186
|
-
return `${acc}; ${key}=${value.value}`;
|
|
187
|
-
}, "");
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Compare if session cookies have actually changed by comparing their values.
|
|
191
|
-
* Ignores expiry timestamps that naturally change on each request.
|
|
192
|
-
*
|
|
193
|
-
* @param prevCookie - Previous cookie JSON string
|
|
194
|
-
* @param newCookie - New cookie JSON string
|
|
195
|
-
* @returns true if session cookies have changed, false otherwise
|
|
196
|
-
*/
|
|
197
|
-
function hasSessionCookieChanged(prevCookie, newCookie) {
|
|
198
|
-
if (!prevCookie) return true;
|
|
199
|
-
try {
|
|
200
|
-
const prev = JSON.parse(prevCookie);
|
|
201
|
-
const next = JSON.parse(newCookie);
|
|
202
|
-
const sessionKeys = /* @__PURE__ */ new Set();
|
|
203
|
-
Object.keys(prev).forEach((key) => {
|
|
204
|
-
if (key.includes("session_token") || key.includes("session_data")) sessionKeys.add(key);
|
|
205
|
-
});
|
|
206
|
-
Object.keys(next).forEach((key) => {
|
|
207
|
-
if (key.includes("session_token") || key.includes("session_data")) sessionKeys.add(key);
|
|
208
|
-
});
|
|
209
|
-
for (const key of sessionKeys) if (prev[key]?.value !== next[key]?.value) return true;
|
|
210
|
-
return false;
|
|
211
|
-
} catch {
|
|
212
|
-
return true;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Check if the Set-Cookie header contains better-auth cookies.
|
|
217
|
-
* This prevents infinite refetching when non-better-auth cookies (like third-party cookies) change.
|
|
218
|
-
*
|
|
219
|
-
* Supports multiple cookie naming patterns:
|
|
220
|
-
* - Default: "better-auth.session_token", "better-auth-passkey", "__Secure-better-auth.session_token"
|
|
221
|
-
* - Custom prefix: "myapp.session_token", "myapp-passkey", "__Secure-myapp.session_token"
|
|
222
|
-
* - Custom full names: "my_custom_session_token", "custom_session_data"
|
|
223
|
-
* - No prefix (cookiePrefix=""): matches any cookie with known suffixes
|
|
224
|
-
* - Multiple prefixes: ["better-auth", "my-app"] matches cookies starting with any of the prefixes
|
|
225
|
-
*
|
|
226
|
-
* @param setCookieHeader - The Set-Cookie header value
|
|
227
|
-
* @param cookiePrefix - The cookie prefix(es) to check for. Can be a string, array of strings, or empty string.
|
|
228
|
-
* @returns true if the header contains better-auth cookies, false otherwise
|
|
229
|
-
*/
|
|
230
|
-
function hasBetterAuthCookies(setCookieHeader, cookiePrefix) {
|
|
231
|
-
const cookies = parseSetCookieHeader(setCookieHeader);
|
|
232
|
-
const cookieSuffixes = ["session_token", "session_data"];
|
|
233
|
-
const prefixes = Array.isArray(cookiePrefix) ? cookiePrefix : [cookiePrefix];
|
|
234
|
-
for (const name of cookies.keys()) {
|
|
235
|
-
const nameWithoutSecure = name.startsWith("__Secure-") ? name.slice(9) : name;
|
|
236
|
-
for (const prefix of prefixes) if (prefix) {
|
|
237
|
-
if (nameWithoutSecure.startsWith(prefix)) return true;
|
|
238
|
-
} else for (const suffix of cookieSuffixes) if (nameWithoutSecure.endsWith(suffix)) return true;
|
|
239
|
-
}
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
//#endregion
|
|
244
|
-
//#region src/setup.ts
|
|
245
|
-
const { app: app$1, session, protocol, BrowserWindow } = electron;
|
|
80
|
+
//#region src/browser.ts
|
|
81
|
+
const { app: app$1, session, protocol, BrowserWindow, ipcMain, webContents: webContents$1 } = electron;
|
|
246
82
|
function withGetWindowFallback(win) {
|
|
247
83
|
return win ?? (() => {
|
|
248
84
|
const allWindows = BrowserWindow.getAllWindows();
|
|
249
85
|
return allWindows.length > 0 ? allWindows[0] : null;
|
|
250
86
|
});
|
|
251
87
|
}
|
|
252
|
-
function setupRenderer(opts) {
|
|
253
|
-
if (!isProcessType("renderer")) throw new BetterAuthError("setupRenderer can only be called in the renderer process.");
|
|
254
|
-
exposeBridges(opts);
|
|
255
|
-
}
|
|
256
88
|
function setupMain($fetch, $store, getCookie, opts, clientOptions, cfg) {
|
|
257
89
|
if (!isProcessType("browser")) throw new BetterAuthError("setupMain can only be called in the main process.");
|
|
258
90
|
if (!cfg || cfg.csp === true) setupCSP(clientOptions);
|
|
@@ -367,6 +199,122 @@ function setupCSP(clientOptions) {
|
|
|
367
199
|
});
|
|
368
200
|
});
|
|
369
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* Sets up IPC bridges in the main process.
|
|
204
|
+
*/
|
|
205
|
+
function setupBridges(ctx, opts, clientOptions) {
|
|
206
|
+
const prefix = getChannelPrefixWithDelimiter(opts.channelPrefix);
|
|
207
|
+
ctx.$store?.atoms.session?.subscribe((state) => {
|
|
208
|
+
if (state.isPending === true) return;
|
|
209
|
+
webContents$1.getFocusedWebContents()?.send(`${prefix}user-updated`, state?.data?.user ?? null);
|
|
210
|
+
});
|
|
211
|
+
ipcMain.handle(`${prefix}getUser`, async () => {
|
|
212
|
+
return (await ctx.$fetch("/get-session", {
|
|
213
|
+
method: "GET",
|
|
214
|
+
headers: {
|
|
215
|
+
cookie: ctx.getCookie(),
|
|
216
|
+
"content-type": "application/json"
|
|
217
|
+
}
|
|
218
|
+
})).data?.user ?? null;
|
|
219
|
+
});
|
|
220
|
+
ipcMain.handle(`${prefix}requestAuth`, (_evt, options) => requestAuth(clientOptions, opts, options));
|
|
221
|
+
ipcMain.handle(`${prefix}signOut`, async () => {
|
|
222
|
+
await ctx.$fetch("/sign-out", {
|
|
223
|
+
method: "POST",
|
|
224
|
+
body: "{}",
|
|
225
|
+
headers: {
|
|
226
|
+
cookie: ctx.getCookie(),
|
|
227
|
+
"content-type": "application/json"
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
//#endregion
|
|
234
|
+
//#region src/cookies.ts
|
|
235
|
+
function getSetCookie(header, prevCookie) {
|
|
236
|
+
const parsed = parseSetCookieHeader(header);
|
|
237
|
+
let toSetCookie = {};
|
|
238
|
+
parsed.forEach((cookie, key) => {
|
|
239
|
+
const expiresAt = cookie["expires"];
|
|
240
|
+
const maxAge = cookie["max-age"];
|
|
241
|
+
const expires = maxAge ? new Date(Date.now() + Number(maxAge) * 1e3) : expiresAt ? new Date(String(expiresAt)) : null;
|
|
242
|
+
toSetCookie[key] = {
|
|
243
|
+
value: cookie["value"],
|
|
244
|
+
expires: expires ? expires.toISOString() : null
|
|
245
|
+
};
|
|
246
|
+
});
|
|
247
|
+
if (prevCookie) try {
|
|
248
|
+
toSetCookie = {
|
|
249
|
+
...JSON.parse(prevCookie),
|
|
250
|
+
...toSetCookie
|
|
251
|
+
};
|
|
252
|
+
} catch {}
|
|
253
|
+
return JSON.stringify(toSetCookie);
|
|
254
|
+
}
|
|
255
|
+
function getCookie(cookie) {
|
|
256
|
+
let parsed = {};
|
|
257
|
+
try {
|
|
258
|
+
parsed = JSON.parse(cookie);
|
|
259
|
+
} catch (_e) {}
|
|
260
|
+
return Object.entries(parsed).reduce((acc, [key, value]) => {
|
|
261
|
+
if (value.expires && new Date(value.expires) < /* @__PURE__ */ new Date()) return acc;
|
|
262
|
+
return `${acc}; ${key}=${value.value}`;
|
|
263
|
+
}, "");
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Compare if session cookies have actually changed by comparing their values.
|
|
267
|
+
* Ignores expiry timestamps that naturally change on each request.
|
|
268
|
+
*
|
|
269
|
+
* @param prevCookie - Previous cookie JSON string
|
|
270
|
+
* @param newCookie - New cookie JSON string
|
|
271
|
+
* @returns true if session cookies have changed, false otherwise
|
|
272
|
+
*/
|
|
273
|
+
function hasSessionCookieChanged(prevCookie, newCookie) {
|
|
274
|
+
if (!prevCookie) return true;
|
|
275
|
+
try {
|
|
276
|
+
const prev = JSON.parse(prevCookie);
|
|
277
|
+
const next = JSON.parse(newCookie);
|
|
278
|
+
const sessionKeys = /* @__PURE__ */ new Set();
|
|
279
|
+
Object.keys(prev).forEach((key) => {
|
|
280
|
+
if (key.includes("session_token") || key.includes("session_data")) sessionKeys.add(key);
|
|
281
|
+
});
|
|
282
|
+
Object.keys(next).forEach((key) => {
|
|
283
|
+
if (key.includes("session_token") || key.includes("session_data")) sessionKeys.add(key);
|
|
284
|
+
});
|
|
285
|
+
for (const key of sessionKeys) if (prev[key]?.value !== next[key]?.value) return true;
|
|
286
|
+
return false;
|
|
287
|
+
} catch {
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Check if the Set-Cookie header contains better-auth cookies.
|
|
293
|
+
* This prevents infinite refetching when non-better-auth cookies (like third-party cookies) change.
|
|
294
|
+
*
|
|
295
|
+
* Supports multiple cookie naming patterns:
|
|
296
|
+
* - Default: "better-auth.session_token", "better-auth-passkey", "__Secure-better-auth.session_token"
|
|
297
|
+
* - Custom prefix: "myapp.session_token", "myapp-passkey", "__Secure-myapp.session_token"
|
|
298
|
+
* - Custom full names: "my_custom_session_token", "custom_session_data"
|
|
299
|
+
* - No prefix (cookiePrefix=""): matches any cookie with known suffixes
|
|
300
|
+
* - Multiple prefixes: ["better-auth", "my-app"] matches cookies starting with any of the prefixes
|
|
301
|
+
*
|
|
302
|
+
* @param setCookieHeader - The Set-Cookie header value
|
|
303
|
+
* @param cookiePrefix - The cookie prefix(es) to check for. Can be a string, array of strings, or empty string.
|
|
304
|
+
* @returns true if the header contains better-auth cookies, false otherwise
|
|
305
|
+
*/
|
|
306
|
+
function hasBetterAuthCookies(setCookieHeader, cookiePrefix) {
|
|
307
|
+
const cookies = parseSetCookieHeader(setCookieHeader);
|
|
308
|
+
const cookieSuffixes = ["session_token", "session_data"];
|
|
309
|
+
const prefixes = Array.isArray(cookiePrefix) ? cookiePrefix : [cookiePrefix];
|
|
310
|
+
for (const name of cookies.keys()) {
|
|
311
|
+
const nameWithoutSecure = name.startsWith("__Secure-") ? name.slice(9) : name;
|
|
312
|
+
for (const prefix of prefixes) if (prefix) {
|
|
313
|
+
if (nameWithoutSecure.startsWith(prefix)) return true;
|
|
314
|
+
} else for (const suffix of cookieSuffixes) if (nameWithoutSecure.endsWith(suffix)) return true;
|
|
315
|
+
}
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
370
318
|
|
|
371
319
|
//#endregion
|
|
372
320
|
//#region src/client.ts
|
|
@@ -464,7 +412,6 @@ const electronClient = (options) => {
|
|
|
464
412
|
return {
|
|
465
413
|
getCookie: getCookieFn,
|
|
466
414
|
requestAuth: (options) => requestAuth(clientOptions, opts, options),
|
|
467
|
-
setupRenderer: () => setupRenderer(opts),
|
|
468
415
|
setupMain: (cfg) => setupMain($fetch, store, getCookieFn, opts, clientOptions, cfg),
|
|
469
416
|
$Infer: {}
|
|
470
417
|
};
|