{"_id":"@dreamshive/better-auth-tauri","_rev":"5-7697591c33f1a0f54a0b9d1885cb2428","name":"@dreamshive/better-auth-tauri","dist-tags":{"latest":"0.1.4"},"versions":{"0.1.0":{"name":"@dreamshive/better-auth-tauri","version":"0.1.0","keywords":["better-auth","tauri","oauth","auth","desktop","deep-link"],"author":{"name":"Rully Ardiansyah"},"license":"MIT","_id":"@dreamshive/better-auth-tauri@0.1.0","maintainers":[{"name":"devoresyah","email":"de.voresyah@gmail.com"}],"homepage":"https://github.com/DreamsHive/better-auth-tauri#readme","bugs":{"url":"https://github.com/DreamsHive/better-auth-tauri/issues"},"dist":{"shasum":"269c9d33a4b38966bdccaaf0c70a25083f250bff","tarball":"https://registry.npmjs.org/@dreamshive/better-auth-tauri/-/better-auth-tauri-0.1.0.tgz","fileCount":38,"integrity":"sha512-ZlnhChU4uVc1a+/shFjAgoNUfRgZuHgscS9E+aMT15Wd3DxL46ygMH6v5QMRk5hrPN2LxalGv3zXvP1ecAQjmA==","signatures":[{"sig":"MEUCIQCtGPY3d/IORJZrAmgG2MP9odPtJzSvEHtwPctZIDpWsgIgYg2v7++r9u2wCY3XCOPsrPV8y/rorXjfX1YYGhiT/44=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":114894},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./client":{"types":"./dist/client.d.ts","import":"./dist/client.js","default":"./dist/client.js"}},"gitHead":"3ec7e05248ccab0e4f8763412c513c57830a84fc","scripts":{"dev":"tsc -p tsconfig.build.json --watch","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"bun run clean && bun run build && bun run typecheck && bun run test"},"_npmUser":{"name":"devoresyah","email":"de.voresyah@gmail.com"},"repository":{"url":"git+https://github.com/DreamsHive/better-auth-tauri.git","type":"git"},"_npmVersion":"11.10.1","description":"Better Auth plugin for Tauri desktop apps — handles OAuth via the system browser with deep-link callbacks, cookie bridging via URL, and secure token storage hooks.","directories":{},"sideEffects":false,"_nodeVersion":"25.7.0","dependencies":{"zod":"^3.24.0 || ^4.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.1.0","typescript":"^5.7.0","@types/node":"^20.0.0","better-auth":"^1.6.5","@tauri-apps/plugin-shell":"^2.0.0","@tauri-apps/plugin-deep-link":"^2.0.0"},"peerDependencies":{"better-auth":"^1.6.0","@tauri-apps/plugin-shell":"^2.0.0","@tauri-apps/plugin-deep-link":"^2.0.0"},"peerDependenciesMeta":{"@tauri-apps/plugin-shell":{"optional":true},"@tauri-apps/plugin-deep-link":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/better-auth-tauri_0.1.0_1776681047961_0.964453020130569","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@dreamshive/better-auth-tauri","version":"0.1.1","keywords":["better-auth","tauri","oauth","auth","desktop","deep-link"],"author":{"name":"Rully Ardiansyah"},"license":"MIT","_id":"@dreamshive/better-auth-tauri@0.1.1","maintainers":[{"name":"devoresyah","email":"de.voresyah@gmail.com"}],"homepage":"https://github.com/DreamsHive/better-auth-tauri#readme","bugs":{"url":"https://github.com/DreamsHive/better-auth-tauri/issues"},"dist":{"shasum":"d3f441483fd95b8ea3b354a6af0a6d39816a4099","tarball":"https://registry.npmjs.org/@dreamshive/better-auth-tauri/-/better-auth-tauri-0.1.1.tgz","fileCount":38,"integrity":"sha512-dLIfE/y1y8m1yV2/YjArrc881ofxoM819tf2atubkTcJZOeTgaesClg0BQExYKh2nG8x3J7lBUDS4l/kY4A4Tw==","signatures":[{"sig":"MEQCIGdGCtOnuWJR11o2ZiS3m+7saQPtyDhTrjrnsd154ZwMAiBY/a2N+EPlDvUl2N4t4elKDekpYVNyGfsb4QAhBDfthA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":115960},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./client":{"types":"./dist/client.d.ts","import":"./dist/client.js","default":"./dist/client.js"}},"gitHead":"a34886e6dba602fec930b74a948c1624de5d2c0d","scripts":{"dev":"tsc -p tsconfig.build.json --watch","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"bun run clean && bun run build && bun run typecheck && bun run test"},"_npmUser":{"name":"devoresyah","email":"de.voresyah@gmail.com"},"repository":{"url":"git+https://github.com/DreamsHive/better-auth-tauri.git","type":"git"},"_npmVersion":"11.12.1","description":"Better Auth plugin for Tauri desktop apps — handles OAuth via the system browser with deep-link callbacks, cookie bridging via URL, and secure token storage hooks.","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","dependencies":{"zod":"^3.24.0 || ^4.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.1.0","typescript":"^5.7.0","@types/node":"^20.0.0","better-auth":"^1.6.5","@tauri-apps/plugin-shell":"^2.0.0","@tauri-apps/plugin-deep-link":"^2.0.0"},"peerDependencies":{"better-auth":"^1.6.0","@tauri-apps/plugin-shell":"^2.0.0","@tauri-apps/plugin-deep-link":"^2.0.0"},"peerDependenciesMeta":{"@tauri-apps/plugin-shell":{"optional":true},"@tauri-apps/plugin-deep-link":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/better-auth-tauri_0.1.1_1784735275784_0.23811309852822538","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@dreamshive/better-auth-tauri","version":"0.1.2","keywords":["better-auth","tauri","oauth","auth","desktop","deep-link"],"author":{"name":"Rully Ardiansyah"},"license":"MIT","_id":"@dreamshive/better-auth-tauri@0.1.2","maintainers":[{"name":"devoresyah","email":"de.voresyah@gmail.com"}],"homepage":"https://github.com/DreamsHive/better-auth-tauri#readme","bugs":{"url":"https://github.com/DreamsHive/better-auth-tauri/issues"},"dist":{"shasum":"730a0f288bb96b03f7cec0b83d076b0a4577e662","tarball":"https://registry.npmjs.org/@dreamshive/better-auth-tauri/-/better-auth-tauri-0.1.2.tgz","fileCount":38,"integrity":"sha512-YLsCZ7ZA5klu9awlqfyRw82CzbMt+mlZntpS/MyuXjCdx1sbbHq0zhFSuVb9BhWcbepYMDCxieL9nyk00tx54A==","signatures":[{"sig":"MEUCIHx8JAfuZu3NZomWM1vu2BI2bGCjDaVpV/6+3aJmtwR4AiEA+KgGbI62zQpOO5pmBV1PLOdEqlql1F9ab7uClZeplLM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dreamshive%2fbetter-auth-tauri@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":116355},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./client":{"types":"./dist/client.d.ts","import":"./dist/client.js","default":"./dist/client.js"}},"gitHead":"bc08411bf7f514c492876040f1c8db60c108b2e9","scripts":{"dev":"tsc -p tsconfig.build.json --watch","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"bun run clean && bun run build && bun run typecheck && bun run test"},"_npmUser":{"name":"devoresyah","email":"de.voresyah@gmail.com"},"repository":{"url":"git+https://github.com/DreamsHive/better-auth-tauri.git","type":"git"},"_npmVersion":"10.8.2","description":"Better Auth plugin for Tauri desktop apps — handles OAuth via the system browser with deep-link callbacks, cookie bridging via URL, and secure token storage hooks.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.2","dependencies":{"zod":"^3.24.0 || ^4.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.42","vitest":"^2.1.0","typescript":"6.0.3","@types/node":"^20.0.0","better-auth":"1.7.3","@tauri-apps/plugin-shell":"^2.0.0","@tauri-apps/plugin-deep-link":"^2.0.0"},"peerDependencies":{"better-auth":"^1.6.0","@tauri-apps/plugin-shell":"^2.0.0","@tauri-apps/plugin-deep-link":"^2.0.0"},"peerDependenciesMeta":{"@tauri-apps/plugin-shell":{"optional":true},"@tauri-apps/plugin-deep-link":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/better-auth-tauri_0.1.2_1788863743770_0.7157823578026097","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@dreamshive/better-auth-tauri","version":"0.1.3","keywords":["better-auth","tauri","oauth","auth","desktop","deep-link"],"author":{"name":"Rully Ardiansyah"},"license":"MIT","_id":"@dreamshive/better-auth-tauri@0.1.3","maintainers":[{"name":"devoresyah","email":"de.voresyah@gmail.com"}],"homepage":"https://github.com/DreamsHive/better-auth-tauri#readme","bugs":{"url":"https://github.com/DreamsHive/better-auth-tauri/issues"},"dist":{"shasum":"e7b3021b50e3a8b01e4f7dad0d771d8c455495cf","tarball":"https://registry.npmjs.org/@dreamshive/better-auth-tauri/-/better-auth-tauri-0.1.3.tgz","fileCount":38,"integrity":"sha512-AbgL2B1ZB7fz9gQ19p/mIBJDwgAkpKYGjqJ/pNX6tp7hJsfPG5u1sRRdakWXdgmrqtHKbN3jz2Z8mUYf/pbBLA==","signatures":[{"sig":"MEQCIE0LSMV6CMpSUKYXgJfWpNw/KISBuUhLDdPmiZxo2rYmAiBmRBRJXAetUgynxNKePpdntU0N8B++bpMpC7LIeVyQhg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dreamshive%2fbetter-auth-tauri@0.1.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":116970},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./client":{"types":"./dist/client.d.ts","import":"./dist/client.js","default":"./dist/client.js"}},"gitHead":"4ad0d8f56b74bf7cac80eaef679edef8de9626a2","scripts":{"dev":"tsc -p tsconfig.build.json --watch","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","test:packed":"node scripts/check-packed-consumer.mjs","prepublishOnly":"bun run clean && bun run build && bun run typecheck && bun run test && bun run test:packed"},"_npmUser":{"name":"devoresyah","email":"de.voresyah@gmail.com"},"repository":{"url":"git+https://github.com/DreamsHive/better-auth-tauri.git","type":"git"},"_npmVersion":"10.8.2","description":"Better Auth plugin for Tauri desktop apps — handles OAuth via the system browser with deep-link callbacks, cookie bridging via URL, and secure token storage hooks.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.2","dependencies":{"zod":"^3.24.0 || ^4.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.42","vitest":"^2.1.0","typescript":"6.0.3","@types/node":"^20.0.0","better-auth":"1.7.3","@tauri-apps/plugin-shell":"^2.0.0","@tauri-apps/plugin-deep-link":"^2.0.0"},"peerDependencies":{"better-auth":"^1.6.0","@tauri-apps/plugin-shell":"^2.0.0","@tauri-apps/plugin-deep-link":"^2.0.0"},"peerDependenciesMeta":{"@tauri-apps/plugin-shell":{"optional":true},"@tauri-apps/plugin-deep-link":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/better-auth-tauri_0.1.3_1788866207778_0.8530416124752682","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"_id":"@dreamshive/better-auth-tauri@0.1.4","bugs":{"url":"https://github.com/DreamsHive/better-auth-tauri/issues"},"dist":{"shasum":"9e352c85d0e23e782a270f4bb6709539d44e664b","tarball":"https://registry.npmjs.org/@dreamshive/better-auth-tauri/-/better-auth-tauri-0.1.4.tgz","fileCount":38,"integrity":"sha512-cv4O319YPvRHhYp/YAjx0e32Or+30e+8KEn2b1RjczUO4Wtl5mgJNZDsWCT6FCD4CIC65NJmZYo6/8u1e6u+VQ==","signatures":[{"sig":"MEUCIQCakCBzG1fZBGXKsMne5HZONSzXr4vy1GyRCc3eXutoWQIgbBb4Zcg7SJfpr9hUYRyftI9tP6r0eOuRj0NXtUQmP4M=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDcif+oxD9/fXo9Ljlg04vPrf+RyrTGbSQhrsmY4XJrmwIhAPHxV+v/2IipypkpG6IhG0JdGgb9UJO6x1EVK/Pbq13M"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dreamshive%2fbetter-auth-tauri@0.1.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":124936},"main":"./dist/index.js","name":"@dreamshive/better-auth-tauri","type":"module","types":"./dist/index.d.ts","author":{"name":"Rully Ardiansyah"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./client":{"types":"./dist/client.d.ts","import":"./dist/client.js","default":"./dist/client.js"}},"gitHead":"9580db7e761f9d7cf687863d10ab34c6fe816080","license":"MIT","scripts":{"dev":"tsc -p tsconfig.build.json --watch","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","test:packed":"node scripts/check-packed-consumer.mjs","prepublishOnly":"bun run clean && bun run build && bun run typecheck && bun run test && bun run test:packed"},"version":"0.1.4","_npmUser":{"name":"devoresyah","email":"de.voresyah@gmail.com"},"homepage":"https://github.com/DreamsHive/better-auth-tauri#readme","keywords":["better-auth","tauri","oauth","auth","desktop","deep-link"],"repository":{"url":"git+https://github.com/DreamsHive/better-auth-tauri.git","type":"git"},"_npmVersion":"10.8.2","description":"Better Auth plugin for Tauri desktop apps — handles OAuth via the system browser with deep-link callbacks, cookie bridging via URL, and secure token storage hooks.","directories":{},"maintainers":[{"name":"devoresyah","email":"de.voresyah@gmail.com"}],"sideEffects":false,"_nodeVersion":"20.20.2","dependencies":{"zod":"^3.24.0 || ^4.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.42","vitest":"^2.1.0","typescript":"6.0.3","@types/node":"^20.0.0","better-auth":"1.7.3","@tauri-apps/plugin-shell":"^2.0.0","@tauri-apps/plugin-deep-link":"^2.0.0"},"peerDependencies":{"better-auth":"^1.6.0","@tauri-apps/plugin-shell":"^2.0.0","@tauri-apps/plugin-deep-link":"^2.0.0"},"peerDependenciesMeta":{"@tauri-apps/plugin-shell":{"optional":true},"@tauri-apps/plugin-deep-link":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/better-auth-tauri_0.1.4_1789299283820_0.14882363364426698"}}},"time":{"created":"2026-04-20T10:30:47.801Z","modified":"2026-09-13T11:34:44.238Z","0.1.0":"2026-04-20T10:30:48.095Z","0.1.1":"2026-07-22T15:47:55.940Z","0.1.2":"2026-09-08T10:35:43.891Z","0.1.3":"2026-09-08T11:16:47.929Z","0.1.4":"2026-09-13T11:34:43.917Z"},"bugs":{"url":"https://github.com/DreamsHive/better-auth-tauri/issues"},"author":{"name":"Rully Ardiansyah"},"license":"MIT","homepage":"https://github.com/DreamsHive/better-auth-tauri#readme","keywords":["better-auth","tauri","oauth","auth","desktop","deep-link"],"repository":{"url":"git+https://github.com/DreamsHive/better-auth-tauri.git","type":"git"},"description":"Better Auth plugin for Tauri desktop apps — handles OAuth via the system browser with deep-link callbacks, cookie bridging via URL, and secure token storage hooks.","maintainers":[{"name":"devoresyah","email":"de.voresyah@gmail.com"}],"readme":"# @dreamshive/better-auth-tauri\n\n[Better Auth](https://better-auth.com) plugin for [Tauri](https://tauri.app) desktop apps. OAuth happens in the user's **system browser**, not the embedded webview — the session cookie rides back through a custom URI scheme deep link, and every outgoing request in the app is transparently authenticated.\n\nMirrors the architecture of the official [`@better-auth/expo`](https://github.com/better-auth/better-auth/tree/main/packages/expo) package, adapted for Tauri's browser-style webview.\n\n## Why use this\n\n- **OAuth providers that block embedded webviews still work** (Google's `disallowed_useragent`, enterprise SSO, upcoming Microsoft enforcement).\n- **Users reuse their existing browser sessions** — no re-logging-in for github.com / google.com from inside your app.\n- **Full address bar + padlock** — users can verify the provider's domain before signing in.\n- **Same Better Auth API surface** — you keep using `authClient.signIn.social(...)` and `useSession()` as if nothing changed.\n\n## How it works\n\n```\n┌──────────────────┐           ┌────────────────┐         ┌──────────────┐\n│  Tauri app       │           │ System browser │         │ Auth service │\n│  (WKWebView/     │           │ (Safari/Chrome)│         │              │\n│   WebView2)      │           │                │         │              │\n└────────┬─────────┘           └────────┬───────┘         └──────┬───────┘\n         │ signIn.social()              │                        │\n         │ ─────────────────────────────┼───────────────────────>│\n         │                              │                        │\n         │ <── { url, disableRedirect } ──────────────────────────│\n         │                              │                        │\n         │ shell.open(url)              │                        │\n         │ ────────────────────────────>│                        │\n         │                              │ /tauri-authz-proxy     │\n         │                              │ ──────────────────────>│\n         │                              │ Set-Cookie: state=...  │\n         │                              │ <──────────────────────│\n         │                              │                        │\n         │                              │ github.com OAuth...    │\n         │                              │ <─redirect to callback─│\n         │                              │ /callback/github       │\n         │                              │ ──────────────────────>│\n         │                              │                        │\n         │                              │ <── 302 sokudo://?cookie=<session>\n         │                              │     (after-hook appends cookie\n         │                              │     to custom-scheme redirect)\n         │                              │                        │\n         │ <── OS routes sokudo://  ────│                        │\n         │                                                       │\n         │ store cookie in OS keychain                           │\n         │ inject as `x-tauri-cookie` header on future calls     │\n         │ ─────────────────────────────────────────────────────>│\n         │                                                       │\n         │ <── server plugin rewrites x-tauri-cookie → Cookie ───│\n         │     Better Auth validates, returns session           │\n```\n\nTwo things worth calling out:\n\n1. **`x-tauri-cookie` header smuggling.** The Fetch spec marks `Cookie` as a [forbidden header name](https://fetch.spec.whatwg.org/#forbidden-header-name). Webviews silently drop attempts to set it. We smuggle the session through `x-tauri-cookie` and the **server** plugin rewrites it back to `Cookie` before Better Auth inspects the request.\n2. **`disableRedirect: true` auto-injection.** Better Auth's Vue/React client normally navigates `window.location.href` to the OAuth URL — fine in a browser, catastrophic in a single-window Tauri app (the webview takes over with the provider's login page). The client plugin injects `disableRedirect: true` into `/sign-in/social` and `/sign-in/oauth2` requests so the client stays put.\n3. **Cookie mutations are serialized.** Response cookies, deep-link callbacks, and signout share one local mutation queue. A delayed response may revoke only the session it was sent with, and an OAuth callback cannot restore a session after explicit signout.\n\n## Installation\n\n```bash\nbun add @dreamshive/better-auth-tauri\n# or: npm install / pnpm add / yarn add\n```\n\nPeer Tauri plugins (install only in the desktop app that consumes the client):\n\n```bash\nbun add @tauri-apps/plugin-shell @tauri-apps/plugin-deep-link\ncd src-tauri\ncargo add tauri-plugin-shell tauri-plugin-deep-link\n```\n\nIf you want the included focus/online managers to work, you also need:\n\n```bash\nbun add @tauri-apps/api   # onFocusChanged lives here\n```\n\n## Tauri configuration\n\n### 1. Register your URI scheme\n\n`src-tauri/tauri.conf.json`:\n\n```json\n{\n  \"plugins\": {\n    \"deep-link\": {\n      \"desktop\": {\n        \"schemes\": [\"yourapp\"]\n      }\n    }\n  }\n}\n```\n\n### 2. Initialize the Rust plugins\n\n`src-tauri/src/lib.rs`:\n\n```rust\npub fn run() {\n  tauri::Builder::default()\n    .plugin(tauri_plugin_shell::init())\n    .plugin(tauri_plugin_deep_link::init())\n    // ... your other plugins\n    .run(tauri::generate_context!())\n    .expect(\"error while running tauri application\");\n}\n```\n\n### 3. Grant permissions\n\n`src-tauri/capabilities/default.json`:\n\n```json\n{\n  \"permissions\": [\n    \"core:default\",\n    \"shell:allow-open\",\n    \"deep-link:default\"\n  ]\n}\n```\n\n### 4. (Highly recommended) Add `tauri-plugin-single-instance`\n\nWithout this, opening a `yourapp://` deep link from a browser while the Tauri app is already running may spawn a second window instead of reusing the first. Install:\n\n```bash\ncd src-tauri\ncargo add tauri-plugin-single-instance --features deep-link\n```\n\nRegister it **before** any other plugin in `lib.rs`:\n\n```rust\nuse tauri::{Emitter, Manager};\n\npub fn run() {\n  tauri::Builder::default()\n    .plugin(tauri_plugin_single_instance::init(|app, argv, _cwd| {\n      if let Some(window) = app.get_webview_window(\"main\") {\n        let _ = window.unminimize();\n        let _ = window.show();\n        let _ = window.set_focus();\n      }\n      for arg in argv.iter().skip(1) {\n        if arg.starts_with(\"yourapp://\") {\n          let _ = app.emit(\"deep-link://new-url\", vec![arg.clone()]);\n        }\n      }\n    }))\n    .plugin(tauri_plugin_shell::init())\n    .plugin(tauri_plugin_deep_link::init())\n    // ...\n}\n```\n\n## Server setup (Better Auth backend)\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { tauri } from \"@dreamshive/better-auth-tauri\";\n\nexport const auth = betterAuth({\n  // ... your existing config\n  trustedOrigins: [\"yourapp://\"],\n  plugins: [\n    tauri(),\n    // ... your other plugins\n  ],\n});\n```\n\nThe server plugin:\n\n1. Remaps `tauri-origin` → `origin` so CSRF / trusted-origin checks accept the custom scheme.\n2. Rewrites `x-tauri-cookie` → `Cookie` so the session cookie smuggled by the client is visible to Better Auth.\n3. Appends `?cookie=<Set-Cookie>` to OAuth redirect URLs targeting custom schemes so the Tauri app can bridge the cookie jar.\n4. Exposes a `/tauri-authorization-proxy` endpoint that plants the OAuth `state` cookie in the system browser's jar before redirecting to the provider (prevents callback state mismatch).\n\n### CORS\n\nYour auth server's CORS config must allow the custom headers this plugin sends:\n\n```ts\ncors({\n  origin: [/* your frontend origins */, \"yourapp://\"],\n  credentials: true,\n  allowedHeaders: [\n    \"Content-Type\",\n    \"Authorization\",\n    \"Cookie\",\n    \"tauri-origin\",\n    \"x-tauri-cookie\",\n    \"x-skip-oauth-proxy\",\n  ],\n})\n```\n\n## Client setup\n\n```ts\nimport { createAuthClient } from \"better-auth/vue\"; // or /react, /solid, etc.\nimport { tauriClient } from \"@dreamshive/better-auth-tauri/client\";\nimport { fetch as tauriFetch } from \"@tauri-apps/plugin-http\";\nimport { isTauri } from \"@tauri-apps/api/core\";\n\nexport const authClient = createAuthClient({\n  baseURL: \"https://auth.example.com\",\n  fetchOptions: {\n    // Always use tauriFetch in Tauri — the webview's native fetch() drops\n    // our smuggled headers and hits CORS on custom schemes.\n    customFetchImpl: (...args) =>\n      isTauri() ? tauriFetch(...args) : fetch(...args),\n  },\n  plugins: [\n    tauriClient({\n      scheme: \"yourapp\",        // must match tauri.conf.json\n      cookiePrefix: \"yourapp\",  // must match your server's cookie prefix\n      storage: {\n        // See \"Secure storage\" below — localStorage is dev-only.\n        getItem: (k) => localStorage.getItem(k),\n        setItem: (k, v) => localStorage.setItem(k, v),\n      },\n    }),\n  ],\n});\n```\n\nThen use Better Auth normally:\n\n```ts\nawait authClient.signIn.social({\n  provider: \"github\",\n  callbackURL: \"/\", // auto-rewritten to yourapp:///\n});\n```\n\nThe plugin takes it from there.\n\n## Secure storage\n\n`localStorage` is **not appropriate for production** — any XSS in your Tauri webview can read it directly.\n\nFor production, back the `storage` option with an encrypted store. Options, ranked by ergonomics:\n\n### Option A — OS keychain (recommended)\n\nExpose `keyring-rs` via custom Tauri commands. Per-platform native secret store (Apple Keychain, Windows Credential Manager, Secret Service on Linux).\n\n```bash\ncd src-tauri\ncargo add keyring --features apple-native,windows-native,linux-native-sync-persistent\n```\n\n```rust\n// src-tauri/src/keyring.rs\nuse keyring::Entry;\n\n#[tauri::command]\npub fn keyring_get(service: String, key: String) -> Result<Option<String>, String> {\n  let entry = Entry::new(&service, &key).map_err(|e| e.to_string())?;\n  match entry.get_password() {\n    Ok(v) => Ok(Some(v)),\n    Err(keyring::Error::NoEntry) => Ok(None),\n    Err(e) => Err(e.to_string()),\n  }\n}\n\n#[tauri::command]\npub fn keyring_set(service: String, key: String, value: String) -> Result<(), String> {\n  let entry = Entry::new(&service, &key).map_err(|e| e.to_string())?;\n  entry.set_password(&value).map_err(|e| e.to_string())\n}\n```\n\nRegister in `lib.rs`:\n\n```rust\nmod keyring;\n\ntauri::Builder::default()\n  .invoke_handler(tauri::generate_handler![\n    keyring::keyring_get,\n    keyring::keyring_set,\n  ])\n```\n\nAnd in your client storage adapter:\n\n```ts\nimport { invoke } from \"@tauri-apps/api/core\";\nconst SERVICE = \"com.yourapp.session\";\n\nconst keyringStorage = {\n  getItem: (key: string) => invoke<string | null>(\"keyring_get\", { service: SERVICE, key }),\n  setItem: (key: string, value: string) => invoke(\"keyring_set\", { service: SERVICE, key, value }),\n};\n```\n\n### Option B — `tauri-plugin-stronghold`\n\nOfficial Tauri encrypted vault (IOTA Stronghold). Requires a master passphrase that the app supplies.\n\n### Option C — `@tauri-apps/plugin-store`\n\nOn-disk JSON, not encrypted, but lives behind Tauri IPC (not accessible from `document.localStorage`). Marginal improvement over `localStorage`.\n\n## Options\n\n### `tauri(options?)` — server plugin\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `disableOriginOverride` | `false` | Don't remap `tauri-origin` → `origin`. |\n\n### `tauriClient(options)` — client plugin\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `scheme` | — (required) | Custom URI scheme registered in `tauri.conf.json`. |\n| `storage` | — (required) | Key/value storage adapter. Async methods supported. |\n| `storagePrefix` | `\"better-auth\"` | Prefix for storage keys. |\n| `cookiePrefix` | `\"better-auth\"` | Server cookie-name prefix(es). |\n| `disableCache` | `false` | Disable local `/get-session` response cache. |\n| `refetchOnWindowFocus` | `true` | Refetch session when the Tauri window regains focus. |\n| `refetchOnReconnect` | `true` | Refetch session when the network comes back online. |\n\n## Exports\n\n```ts\n// Server\nimport { tauri, tauriAuthorizationProxy } from \"@dreamshive/better-auth-tauri\";\n\n// Client\nimport {\n  tauriClient,\n  setupTauriFocusManager,   // manual wiring if you disabled auto-setup\n  setupTauriOnlineManager,\n} from \"@dreamshive/better-auth-tauri/client\";\n```\n\n## macOS dev workflow\n\nDeep-link URI schemes on macOS are registered with Launch Services through the app's `.app` bundle's `Info.plist`. **`tauri dev` does not produce a bundle** — it runs a raw binary which macOS can't route deep links to.\n\nOfficial Tauri guidance for developing against the deep-link plugin on macOS:\n\n```bash\n# Build once (also run after any Rust/plugin/capability change)\nbun tauri build --debug\n# Open the bundle so Launch Services indexes it\nopen src-tauri/target/debug/bundle/macos/your-app.app\n```\n\nFrontend (Vue/React/Svelte) HMR works fine through the built app if you point `frontendDist` at your dev server:\n\n```json\n// src-tauri/tauri.dev.conf.json\n{\n  \"$schema\": \"../node_modules/@tauri-apps/cli/config.schema.json\",\n  \"build\": {\n    \"frontendDist\": \"http://localhost:3000\",\n    \"beforeBuildCommand\": \"\"\n  }\n}\n```\n\n```bash\nbun tauri build --debug --config src-tauri/tauri.dev.conf.json\n```\n\n## Known limitations\n\n- **macOS dev loop requires a bundle** (see above). Windows and Linux don't have this friction.\n- **Scheme hijacking** — any app can register itself as a handler for your custom scheme. Production apps with meaningful attack surface should consider Universal Links (macOS) or App Links (Android equivalent).\n- **Storage is the caller's responsibility.** This plugin doesn't bundle a secure-storage primitive; you provide the adapter.\n- **No SSR support.** Tauri apps are client-only.\n\n## Comparison to `@better-auth/expo`\n\nFeature-for-feature parity on the auth flow, plus two Tauri-specific additions:\n\n| Feature | Expo | Tauri (this) |\n| --- | --- | --- |\n| OAuth via system browser | ✅ | ✅ |\n| Cookie bridge via URL query param | ✅ | ✅ |\n| Authorization proxy endpoint for state | ✅ | ✅ |\n| Redirect-to-scheme callback rewrite | ✅ | ✅ |\n| Sign-out local cleanup | ✅ | ✅ |\n| Third-party cookie filter | ✅ | ✅ |\n| Focus refetch manager | ✅ (React Query) | ✅ (notifies Better Auth session signal) |\n| Online refetch manager | ✅ (React Query) | ✅ (notifies Better Auth session signal) |\n| **`x-tauri-cookie` smuggling** | — | ✅ required (browser forbids `Cookie`) |\n| **`disableRedirect: true` injection** | — | ✅ required (browser has `window.location`) |\n\n## License\n\nMIT © [Rully Ardiansyah](https://github.com/DreamsHive)\n","readmeFilename":"README.md"}