@dreamshive/better-auth-tauri 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/dist/client.js ADDED
@@ -0,0 +1,377 @@
1
+ import { parseSetCookieHeader, SECURE_COOKIE_PREFIX, stripSecureCookiePrefix, } from "better-auth/cookies";
2
+ import { createSchemeURL, getOrigin, isTauriRuntime, safeJSONParse } from "./utils";
3
+ import { setupTauriFocusManager } from "./focus-manager";
4
+ import { setupTauriOnlineManager } from "./online-manager";
5
+ import { PACKAGE_VERSION } from "./version";
6
+ /* -------------------------------------------------------------------------- */
7
+ /* Cookie jar helpers */
8
+ /* -------------------------------------------------------------------------- */
9
+ function getSetCookie(header, prevCookie) {
10
+ const parsed = parseSetCookieHeader(header);
11
+ const toSetCookie = safeJSONParse(prevCookie) ?? {};
12
+ parsed.forEach((cookie, key) => {
13
+ const expiresAt = cookie["expires"];
14
+ const maxAge = cookie["max-age"];
15
+ if (maxAge !== undefined && Number(maxAge) <= 0) {
16
+ delete toSetCookie[key];
17
+ return;
18
+ }
19
+ const expires = maxAge
20
+ ? new Date(Date.now() + Number(maxAge) * 1000)
21
+ : expiresAt
22
+ ? new Date(String(expiresAt))
23
+ : null;
24
+ if (expires && expires.getTime() <= Date.now()) {
25
+ delete toSetCookie[key];
26
+ return;
27
+ }
28
+ toSetCookie[key] = {
29
+ value: cookie["value"],
30
+ expires: expires ? expires.toISOString() : null,
31
+ };
32
+ });
33
+ return JSON.stringify(toSetCookie);
34
+ }
35
+ function getCookie(cookie) {
36
+ const parsed = safeJSONParse(cookie) ?? {};
37
+ return Object.entries(parsed).reduce((acc, [key, value]) => {
38
+ if (value.expires && new Date(value.expires) < new Date())
39
+ return acc;
40
+ return acc ? `${acc}; ${key}=${value.value}` : `${key}=${value.value}`;
41
+ }, "");
42
+ }
43
+ function getOAuthStateValue(cookieJson, cookiePrefix) {
44
+ if (!cookieJson)
45
+ return null;
46
+ const parsed = safeJSONParse(cookieJson);
47
+ if (!parsed)
48
+ return null;
49
+ const prefixes = Array.isArray(cookiePrefix) ? cookiePrefix : [cookiePrefix];
50
+ for (const prefix of prefixes) {
51
+ for (const name of [
52
+ `${SECURE_COOKIE_PREFIX}${prefix}.oauth_state`,
53
+ `${prefix}.oauth_state`,
54
+ ]) {
55
+ const value = parsed?.[name]?.value;
56
+ if (value)
57
+ return value;
58
+ }
59
+ }
60
+ return null;
61
+ }
62
+ /** Only notify `$sessionSignal` when the session cookie values actually changed. */
63
+ function hasSessionCookieChanged(prevCookie, newCookie) {
64
+ if (!prevCookie)
65
+ return true;
66
+ try {
67
+ const prev = JSON.parse(prevCookie);
68
+ const next = JSON.parse(newCookie);
69
+ const keys = new Set();
70
+ for (const k of Object.keys(prev)) {
71
+ if (k.includes("session_token") || k.includes("session_data"))
72
+ keys.add(k);
73
+ }
74
+ for (const k of Object.keys(next)) {
75
+ if (k.includes("session_token") || k.includes("session_data"))
76
+ keys.add(k);
77
+ }
78
+ for (const k of keys) {
79
+ if (prev[k]?.value !== next[k]?.value)
80
+ return true;
81
+ }
82
+ return false;
83
+ }
84
+ catch {
85
+ return true;
86
+ }
87
+ }
88
+ /** Is this Set-Cookie header ours, or third-party (Cloudflare, analytics)? */
89
+ function hasBetterAuthCookies(setCookieHeader, cookiePrefix) {
90
+ const cookies = parseSetCookieHeader(setCookieHeader);
91
+ const prefixes = Array.isArray(cookiePrefix) ? cookiePrefix : [cookiePrefix];
92
+ const suffixes = ["session_token", "session_data"];
93
+ for (const name of cookies.keys()) {
94
+ const bare = stripSecureCookiePrefix(name);
95
+ for (const prefix of prefixes) {
96
+ if (prefix) {
97
+ if (bare.startsWith(prefix))
98
+ return true;
99
+ }
100
+ else {
101
+ for (const s of suffixes)
102
+ if (bare.endsWith(s))
103
+ return true;
104
+ }
105
+ }
106
+ }
107
+ return false;
108
+ }
109
+ /** Some storage backends (keychains) reject `:` in keys. */
110
+ function normalizeKey(name) {
111
+ return name.replace(/:/g, "_");
112
+ }
113
+ function wrapStorage(storage) {
114
+ return {
115
+ async getItem(name) {
116
+ const v = await storage.getItem(normalizeKey(name));
117
+ return v ?? null;
118
+ },
119
+ async setItem(name, value) {
120
+ await storage.setItem(normalizeKey(name), value);
121
+ },
122
+ };
123
+ }
124
+ /* -------------------------------------------------------------------------- */
125
+ /* Deep-link OAuth flow */
126
+ /* -------------------------------------------------------------------------- */
127
+ /**
128
+ * Open a URL in the user's default system browser and wait for the
129
+ * `{scheme}://` deep-link callback. Resolves with the full callback URL
130
+ * (which includes `?cookie=...` thanks to the server plugin's `after` hook).
131
+ */
132
+ async function openAuthSession(urlToOpen, scheme) {
133
+ // Peer-deps are resolved lazily so the package can be imported in a
134
+ // non-Tauri context (e.g. `bun dev` in a browser) without a hard failure.
135
+ const [{ open: openShell }, { onOpenUrl }] = await Promise.all([
136
+ import("@tauri-apps/plugin-shell"),
137
+ import("@tauri-apps/plugin-deep-link"),
138
+ ]);
139
+ const callback = new Promise((resolve, reject) => {
140
+ let settled = false;
141
+ const timeout = setTimeout(() => {
142
+ if (settled)
143
+ return;
144
+ settled = true;
145
+ reject(new Error("OAuth timed out — no deep-link callback received"));
146
+ }, 5 * 60 * 1000);
147
+ // `onOpenUrl` returns a Promise<UnlistenFn>. We unlisten as soon as we get
148
+ // a URL that matches our scheme so we don't leak event subscriptions.
149
+ const unlistenPromise = onOpenUrl((urls) => {
150
+ const match = urls.find((u) => u.startsWith(`${scheme}://`));
151
+ if (!match)
152
+ return;
153
+ if (settled)
154
+ return;
155
+ settled = true;
156
+ clearTimeout(timeout);
157
+ unlistenPromise.then((fn) => fn()).catch(() => { });
158
+ resolve(match);
159
+ });
160
+ });
161
+ await openShell(urlToOpen);
162
+ return callback;
163
+ }
164
+ /* -------------------------------------------------------------------------- */
165
+ /* Client plugin */
166
+ /* -------------------------------------------------------------------------- */
167
+ export const tauriClient = (opts) => {
168
+ if (!opts.scheme) {
169
+ throw new Error("[better-auth-tauri] `scheme` is required. Pass the custom URI scheme " +
170
+ "you registered in tauri.conf.json (e.g. 'sokudo').");
171
+ }
172
+ const storagePrefix = opts.storagePrefix || "better-auth";
173
+ const cookieName = `${storagePrefix}_cookie`;
174
+ const localCacheName = `${storagePrefix}_session_data`;
175
+ const cookiePrefix = opts.cookiePrefix || "better-auth";
176
+ const storage = wrapStorage(opts.storage);
177
+ const scheme = opts.scheme;
178
+ const refetchOnWindowFocus = opts.refetchOnWindowFocus !== false;
179
+ const refetchOnReconnect = opts.refetchOnReconnect !== false;
180
+ let store = null;
181
+ return {
182
+ id: "tauri",
183
+ getActions(_, $store) {
184
+ store = $store;
185
+ // Wire up focus + online refetch managers once the store is
186
+ // available. No-op outside Tauri runtime. These fire-and-forget —
187
+ // the subscriptions live for the process lifetime.
188
+ if (isTauriRuntime() && store) {
189
+ if (refetchOnWindowFocus) {
190
+ setupTauriFocusManager(store).catch(() => { });
191
+ }
192
+ if (refetchOnReconnect) {
193
+ setupTauriOnlineManager(store);
194
+ }
195
+ }
196
+ return {
197
+ /**
198
+ * Returns the currently-stored cookie string in the standard
199
+ * `key=value; key2=value2` format — useful if you need to attach it
200
+ * to a custom fetch outside of the Better Auth client.
201
+ */
202
+ getCookie: async () => {
203
+ const raw = await storage.getItem(cookieName);
204
+ return getCookie(raw || "{}");
205
+ },
206
+ };
207
+ },
208
+ fetchPlugins: [
209
+ {
210
+ id: "tauri",
211
+ name: "Tauri",
212
+ hooks: {
213
+ async onSuccess(context) {
214
+ // In a plain-browser dev environment the default cookie flow
215
+ // works fine — skip the deep-link machinery.
216
+ if (!isTauriRuntime())
217
+ return;
218
+ const setCookieHeader = context.response.headers.get("set-cookie");
219
+ if (setCookieHeader) {
220
+ if (hasBetterAuthCookies(setCookieHeader, cookiePrefix)) {
221
+ const prev = await storage.getItem(cookieName);
222
+ const next = getSetCookie(setCookieHeader, prev ?? undefined);
223
+ await storage.setItem(cookieName, next);
224
+ if (hasSessionCookieChanged(prev, next)) {
225
+ store?.notify("$sessionSignal");
226
+ }
227
+ }
228
+ }
229
+ if (context.request.url.toString().includes("/get-session") &&
230
+ !opts.disableCache) {
231
+ await storage.setItem(localCacheName, JSON.stringify(context.data));
232
+ }
233
+ // Detect social / generic-oauth sign-in responses that include
234
+ // an authorization URL. We check the URL instead of the
235
+ // `redirect` flag because our init hook sets
236
+ // `disableRedirect: true` on these requests (to stop Better
237
+ // Auth's Vue client from window.location-navigating the Tauri
238
+ // webview), which causes the server to respond with
239
+ // `redirect: false`. The presence of `url` is the reliable
240
+ // signal that this is an OAuth start response.
241
+ const requestURL = context.request.url.toString();
242
+ const isSignInRedirect = typeof context.data?.url === "string" &&
243
+ (requestURL.includes("/sign-in/social") ||
244
+ requestURL.includes("/sign-in/oauth2") ||
245
+ requestURL.includes("/link-social"));
246
+ if (!isSignInRedirect)
247
+ return;
248
+ const bodyStr = typeof context.request?.body === "string"
249
+ ? context.request.body
250
+ : JSON.stringify(context.request?.body ?? {});
251
+ if (bodyStr.includes("idToken"))
252
+ return; // silent native flow
253
+ const signInURL = context.data.url;
254
+ // Prevent Better Auth's Vue/React client from auto-navigating
255
+ // the Tauri webview to the OAuth URL via window.location.href.
256
+ // On web this is the correct default; in a Tauri webview it
257
+ // would take over the app's own window with the provider's
258
+ // login page. We open the system browser ourselves below.
259
+ context.data.redirect = false;
260
+ context.data.url = undefined;
261
+ // Route the system-browser navigation through the server-side
262
+ // `tauriAuthorizationProxy` endpoint so the OAuth `state` cookie
263
+ // gets planted in the browser's cookie jar before the provider
264
+ // redirect. Without this, Better Auth's callback state check
265
+ // fails because the state cookie was set on the /sign-in/social
266
+ // response inside the Tauri webview — a different cookie jar.
267
+ //
268
+ // If we have a previously-stored `oauth_state` (from an earlier
269
+ // sign-in attempt), forward it to the proxy so it can be
270
+ // re-seeded instead of re-derived from the URL.
271
+ const storedCookieJson = await storage.getItem(cookieName);
272
+ const oauthStateValue = getOAuthStateValue(storedCookieJson, cookiePrefix);
273
+ const params = new URLSearchParams({
274
+ authorizationURL: signInURL,
275
+ });
276
+ if (oauthStateValue) {
277
+ params.append("oauthState", oauthStateValue);
278
+ }
279
+ const proxyURL = `${context.request.baseURL}/tauri-authorization-proxy?${params.toString()}`;
280
+ try {
281
+ const callbackURL = await openAuthSession(proxyURL, scheme);
282
+ const parsed = new URL(callbackURL);
283
+ const cookie = parsed.searchParams.get("cookie");
284
+ if (!cookie)
285
+ return;
286
+ const prev = await storage.getItem(cookieName);
287
+ const next = getSetCookie(cookie, prev ?? undefined);
288
+ await storage.setItem(cookieName, next);
289
+ store?.notify("$sessionSignal");
290
+ }
291
+ catch (err) {
292
+ // Re-throw so the caller of signIn.social() sees the failure.
293
+ throw err;
294
+ }
295
+ },
296
+ },
297
+ async init(url, options) {
298
+ if (!isTauriRuntime()) {
299
+ return { url, options };
300
+ }
301
+ options = options || {};
302
+ options.credentials = "omit";
303
+ // Native ID-token flow (e.g. Sign in with Apple) doesn't need
304
+ // cookie/origin handling — the token is verified server-side.
305
+ const isIdTokenRequest = options.body?.idToken !==
306
+ undefined;
307
+ if (isIdTokenRequest) {
308
+ options.headers = {
309
+ ...options.headers,
310
+ "x-skip-oauth-proxy": "true",
311
+ };
312
+ }
313
+ else {
314
+ const stored = await storage.getItem(cookieName);
315
+ const cookie = getCookie(stored || "{}");
316
+ options.headers = {
317
+ ...options.headers,
318
+ // "Cookie" is a forbidden header name per the Fetch spec
319
+ // and gets silently dropped by the Headers constructor
320
+ // (which runs inside `new Request(...)` before the request
321
+ // ever reaches Rust / tauriFetch). Smuggle the value under
322
+ // a custom header and let the server plugin rewrite it to
323
+ // "Cookie" inside onRequest — round-trip equivalent.
324
+ ...(cookie ? { "x-tauri-cookie": cookie } : {}),
325
+ "tauri-origin": getOrigin(scheme),
326
+ "x-skip-oauth-proxy": "true",
327
+ };
328
+ // Rewrite any relative callbackURL the caller passed as a path
329
+ // (e.g. "/") to the Tauri-facing custom-scheme deep link, so
330
+ // Better Auth's final redirect lands on our deep-link handler.
331
+ const body = options.body;
332
+ if (body) {
333
+ for (const key of [
334
+ "callbackURL",
335
+ "newUserCallbackURL",
336
+ "errorCallbackURL",
337
+ ]) {
338
+ const value = body[key];
339
+ if (typeof value === "string" && value.startsWith("/")) {
340
+ body[key] = createSchemeURL(value, scheme);
341
+ }
342
+ }
343
+ // For social/oauth sign-in routes, force `disableRedirect`
344
+ // so Better Auth's Vue/React client does NOT navigate the
345
+ // Tauri webview to the OAuth provider URL. We open the
346
+ // system browser ourselves in the onSuccess hook below.
347
+ // Without this, the webview takes over with the provider's
348
+ // login page and the user's own app UI is lost.
349
+ if (url.includes("/sign-in/social") ||
350
+ url.includes("/sign-in/oauth2") ||
351
+ url.includes("/link-social")) {
352
+ body.disableRedirect = true;
353
+ }
354
+ }
355
+ // Clear local state on sign-out so the app immediately reflects
356
+ // the logged-out state.
357
+ if (url.includes("/sign-out")) {
358
+ await storage.setItem(cookieName, "{}");
359
+ await storage.setItem(localCacheName, "{}");
360
+ store?.atoms?.session?.set({
361
+ ...store.atoms.session.get(),
362
+ data: null,
363
+ error: null,
364
+ isPending: false,
365
+ });
366
+ }
367
+ }
368
+ return { url, options };
369
+ },
370
+ },
371
+ ],
372
+ };
373
+ };
374
+ export { PACKAGE_VERSION } from "./version";
375
+ export { setupTauriFocusManager } from "./focus-manager";
376
+ export { setupTauriOnlineManager } from "./online-manager";
377
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { ClientStore } from "better-auth/client";
2
+ /**
3
+ * Refetch Better Auth's session whenever the Tauri window regains focus.
4
+ *
5
+ * Rationale: if the user signs out in another window, or their session
6
+ * expires while the app is backgrounded, we want the next time they come
7
+ * back to the app to reflect reality. Without a focus listener the local
8
+ * session store stays stale until some other request triggers a refresh.
9
+ *
10
+ * Subscribes to `@tauri-apps/api/window#onFocusChanged`. On `focused ===
11
+ * true`, we notify `$sessionSignal` which every `useSession` subscriber
12
+ * listens for — causing them to refetch `/get-session`.
13
+ *
14
+ * Returns an unlisten function. Cleanup is the caller's responsibility
15
+ * (typically the plugin's lifecycle handles this implicitly — when the
16
+ * app quits, the listener dies with the process).
17
+ */
18
+ export declare function setupTauriFocusManager(store: ClientStore): Promise<() => void>;
19
+ //# sourceMappingURL=focus-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focus-manager.d.ts","sourceRoot":"","sources":["../src/focus-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,MAAM,IAAI,CAAC,CAerB"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Refetch Better Auth's session whenever the Tauri window regains focus.
3
+ *
4
+ * Rationale: if the user signs out in another window, or their session
5
+ * expires while the app is backgrounded, we want the next time they come
6
+ * back to the app to reflect reality. Without a focus listener the local
7
+ * session store stays stale until some other request triggers a refresh.
8
+ *
9
+ * Subscribes to `@tauri-apps/api/window#onFocusChanged`. On `focused ===
10
+ * true`, we notify `$sessionSignal` which every `useSession` subscriber
11
+ * listens for — causing them to refetch `/get-session`.
12
+ *
13
+ * Returns an unlisten function. Cleanup is the caller's responsibility
14
+ * (typically the plugin's lifecycle handles this implicitly — when the
15
+ * app quits, the listener dies with the process).
16
+ */
17
+ export async function setupTauriFocusManager(store) {
18
+ try {
19
+ const { getCurrentWindow } = await import("@tauri-apps/api/window");
20
+ const win = getCurrentWindow();
21
+ const unlisten = await win.onFocusChanged(({ payload: focused }) => {
22
+ if (focused) {
23
+ store.notify("$sessionSignal");
24
+ }
25
+ });
26
+ return unlisten;
27
+ }
28
+ catch {
29
+ // @tauri-apps/api/window isn't available (e.g. running in a plain
30
+ // browser during `bun dev`). Return a no-op cleanup.
31
+ return () => { };
32
+ }
33
+ }
34
+ //# sourceMappingURL=focus-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focus-manager.js","sourceRoot":"","sources":["../src/focus-manager.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,KAAkB;IAElB,IAAI,CAAC;QACH,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;QACpE,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE;YACjE,IAAI,OAAO,EAAE,CAAC;gBACZ,KAAK,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;YACjC,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,kEAAkE;QAClE,qDAAqD;QACrD,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { BetterAuthPlugin } from "better-auth";
2
+ export interface TauriOptions {
3
+ /**
4
+ * Disable the origin header override for Tauri requests.
5
+ *
6
+ * Normally the plugin maps the `tauri-origin` header (sent by the client
7
+ * plugin) to the `origin` header so the server's trusted-origin checks
8
+ * accept the custom URI scheme. Set this to `true` if you want to handle
9
+ * origin validation yourself.
10
+ */
11
+ disableOriginOverride?: boolean | undefined;
12
+ }
13
+ /**
14
+ * Server-side Better Auth plugin for Tauri desktop apps.
15
+ *
16
+ * What it does:
17
+ * 1. Remaps `tauri-origin` → `origin` so the Better Auth CSRF / trusted-origin
18
+ * check sees a value the server is configured to accept (e.g. `sokudo://`).
19
+ * 2. Intercepts OAuth callback redirects whose `location` targets a non-HTTP
20
+ * custom scheme (the Tauri app's URI scheme). Before the 302 leaves the
21
+ * server, it appends the freshly-set `Set-Cookie` header as a `cookie`
22
+ * query parameter. The Tauri app then reads the cookie out of the
23
+ * deep-link URL and stores it locally — bridging the browser ↔ app
24
+ * cookie jars that the OS keeps isolated.
25
+ *
26
+ * Pair this with `tauriClient` from `@dreamshive/better-auth-tauri/client`.
27
+ */
28
+ export declare const tauri: (options?: TauriOptions) => BetterAuthPlugin;
29
+ export { PACKAGE_VERSION } from "./version";
30
+ export { tauriAuthorizationProxy } from "./routes";
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,MAAM,WAAW,YAAY;IAC3B;;;;;;;OAOG;IACH,qBAAqB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC7C;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,KAAK,GAAI,UAAU,YAAY,KAAG,gBAyG9C,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,119 @@
1
+ import { createAuthMiddleware } from "better-auth/api";
2
+ import { tauriAuthorizationProxy } from "./routes";
3
+ /**
4
+ * Server-side Better Auth plugin for Tauri desktop apps.
5
+ *
6
+ * What it does:
7
+ * 1. Remaps `tauri-origin` → `origin` so the Better Auth CSRF / trusted-origin
8
+ * check sees a value the server is configured to accept (e.g. `sokudo://`).
9
+ * 2. Intercepts OAuth callback redirects whose `location` targets a non-HTTP
10
+ * custom scheme (the Tauri app's URI scheme). Before the 302 leaves the
11
+ * server, it appends the freshly-set `Set-Cookie` header as a `cookie`
12
+ * query parameter. The Tauri app then reads the cookie out of the
13
+ * deep-link URL and stores it locally — bridging the browser ↔ app
14
+ * cookie jars that the OS keeps isolated.
15
+ *
16
+ * Pair this with `tauriClient` from `@dreamshive/better-auth-tauri/client`.
17
+ */
18
+ export const tauri = (options) => {
19
+ return {
20
+ id: "tauri",
21
+ init: () => {
22
+ // In development we trust the common tauri dev origin. The real app
23
+ // scheme (e.g. `sokudo://`) should be added explicitly by the host
24
+ // app in its `trustedOrigins` — we don't assume a scheme here.
25
+ const trustedOrigins = typeof process !== "undefined" &&
26
+ process.env?.NODE_ENV === "development"
27
+ ? ["tauri://localhost"]
28
+ : [];
29
+ return {
30
+ options: {
31
+ trustedOrigins,
32
+ },
33
+ };
34
+ },
35
+ async onRequest(request, _ctx) {
36
+ const tauriOrigin = request.headers.get("tauri-origin");
37
+ // `Cookie` is a forbidden header name on the client side (browsers
38
+ // / WKWebView strip it before sending), so the client plugin
39
+ // smuggles it as `x-tauri-cookie`. We move it back to `Cookie`
40
+ // here so downstream Better Auth handlers see the session cookie.
41
+ const smuggledCookie = request.headers.get("x-tauri-cookie");
42
+ const shouldRewriteOrigin = !options?.disableOriginOverride &&
43
+ !request.headers.get("origin") &&
44
+ !!tauriOrigin;
45
+ if (!shouldRewriteOrigin && !smuggledCookie)
46
+ return;
47
+ const applyRewrites = (headers) => {
48
+ if (shouldRewriteOrigin && tauriOrigin) {
49
+ headers.set("origin", tauriOrigin);
50
+ }
51
+ if (smuggledCookie) {
52
+ headers.set("cookie", smuggledCookie);
53
+ headers.delete("x-tauri-cookie");
54
+ }
55
+ };
56
+ try {
57
+ // Prefer in-place mutation (works on Bun, Node, Deno).
58
+ applyRewrites(request.headers);
59
+ return { request };
60
+ }
61
+ catch {
62
+ // Some runtimes (e.g. Cloudflare Workers) have immutable request
63
+ // headers — fall back to constructing a new Request.
64
+ const newHeaders = new Headers(request.headers);
65
+ applyRewrites(newHeaders);
66
+ return { request: new Request(request, { headers: newHeaders }) };
67
+ }
68
+ },
69
+ hooks: {
70
+ after: [
71
+ {
72
+ matcher(context) {
73
+ return !!(context.path?.startsWith("/callback") ||
74
+ context.path?.startsWith("/oauth2/callback") ||
75
+ context.path?.startsWith("/magic-link/verify") ||
76
+ context.path?.startsWith("/verify-email"));
77
+ },
78
+ handler: createAuthMiddleware(async (ctx) => {
79
+ const headers = ctx.context.responseHeaders;
80
+ const location = headers?.get("location");
81
+ if (!location)
82
+ return;
83
+ // Leave Better Auth's own oauth-proxy plugin redirects alone —
84
+ // those go through a separate round-trip we shouldn't rewrite.
85
+ if (location.includes("/oauth-proxy-callback"))
86
+ return;
87
+ let redirectURL;
88
+ try {
89
+ redirectURL = new URL(location);
90
+ }
91
+ catch {
92
+ return;
93
+ }
94
+ // Only rewrite redirects going to custom schemes — leave HTTP(S)
95
+ // redirects alone (those are the in-browser / web flow).
96
+ const isHttpRedirect = redirectURL.protocol === "http:" ||
97
+ redirectURL.protocol === "https:";
98
+ if (isHttpRedirect)
99
+ return;
100
+ if (!ctx.context.isTrustedOrigin(location))
101
+ return;
102
+ const cookie = headers?.get("set-cookie");
103
+ if (!cookie)
104
+ return;
105
+ redirectURL.searchParams.set("cookie", cookie);
106
+ ctx.setHeader("location", redirectURL.toString());
107
+ }),
108
+ },
109
+ ],
110
+ },
111
+ endpoints: {
112
+ tauriAuthorizationProxy,
113
+ },
114
+ options,
115
+ };
116
+ };
117
+ export { PACKAGE_VERSION } from "./version";
118
+ export { tauriAuthorizationProxy } from "./routes";
119
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { ClientStore } from "better-auth/client";
2
+ /**
3
+ * Refetch the session when the network comes back online. Useful after
4
+ * suspend/resume or a flaky wifi transition — otherwise the session
5
+ * store would keep showing the cached pre-disconnect state until some
6
+ * other request tries (and fails) to reach the server.
7
+ *
8
+ * Uses the browser's standard `online` event, which the Tauri WebView
9
+ * exposes the same way Chrome/Safari do. No Tauri plugin needed — the
10
+ * OS-level network state flows through the webview's navigator.
11
+ *
12
+ * Returns an unlisten function.
13
+ */
14
+ export declare function setupTauriOnlineManager(store: ClientStore): () => void;
15
+ //# sourceMappingURL=online-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"online-manager.d.ts","sourceRoot":"","sources":["../src/online-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,IAAI,CActE"}