@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/LICENSE +21 -0
- package/README.md +401 -0
- package/dist/client.d.ts +181 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +377 -0
- package/dist/client.js.map +1 -0
- package/dist/focus-manager.d.ts +19 -0
- package/dist/focus-manager.d.ts.map +1 -0
- package/dist/focus-manager.js +34 -0
- package/dist/focus-manager.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +119 -0
- package/dist/index.js.map +1 -0
- package/dist/online-manager.d.ts +15 -0
- package/dist/online-manager.d.ts.map +1 -0
- package/dist/online-manager.js +25 -0
- package/dist/online-manager.js.map +1 -0
- package/dist/routes.d.ts +46 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +51 -0
- package/dist/routes.js.map +1 -0
- package/dist/utils.d.ts +21 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +41 -0
- package/dist/utils.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/package.json +81 -0
- package/src/client.ts +491 -0
- package/src/focus-manager.ts +36 -0
- package/src/index.ts +140 -0
- package/src/online-manager.ts +29 -0
- package/src/routes.ts +67 -0
- package/src/utils.ts +42 -0
- package/src/version.ts +1 -0
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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|