Loading...
Loading...
Integration guide for @daveyplate/better-auth-tauri - cookie-based auth in Tauri v2 desktop apps via deep links. Use when setting up Better Auth in a Tauri application, configuring social OAuth for desktop, or wiring up deep link authentication flows.
npx skill4agent add arthrod/conejo-skills better-auth-tauri-setup@daveyplate/better-auth-tauri1. User clicks "Sign in with Google" in Tauri app
2. signInSocial() sends request to /api/auth/sign-in/social with Platform header
3. Server middleware appends scheme callback to OAuth redirectURI:
→ /api/auth/callback/google?callbackURL=my-app://
4. Server returns Google OAuth URL; client opens it in default browser
5. User authenticates in browser; Google redirects to callback URL
6. Server middleware intercepts callbackURL with scheme prefix
7. Server redirects to /api/auth/callback/success?redirectTo=my-app://api/auth/callback/google?...
8. Success page HTML triggers: window.location.href = 'my-app://...'
9. OS deep link activates Tauri app
10. Client handleAuthDeepLink() calls authClient.$fetch() to process callback
11. Server validates OAuth code, creates session, sets cookie
12. onSuccess callback fires - auth completebun add @tauri-apps/api @tauri-apps/plugin-deep-link @tauri-apps/plugin-http @tauri-apps/plugin-os @tauri-apps/plugin-opener better-auth| Plugin | Min Version | Purpose |
|---|---|---|
| >=2.5.0 | Core Tauri API, |
| >=2.2.1 | URL scheme handling ( |
| >=2.4.3 | HTTP with cookie support (macOS fix) |
| >=2.2.1 | Platform detection |
| >=2.2.6 | Open OAuth URLs in default browser |
| >=1.2.7 | Authentication framework |
tauri.conf.json{
"plugins": {
"deep-link": {
"desktop": {
"schemes": ["my-app"]
}
}
}
}import { betterAuth } from "better-auth"
import { tauri } from "@daveyplate/better-auth-tauri/plugin"
export const auth = betterAuth({
// ... your existing config
plugins: [
tauri({
scheme: "my-app", // Must match tauri.conf.json and client
callbackURL: "/", // Where to redirect after auth (default: "/")
successText: "Authentication successful! You can close this window.",
successURL: undefined, // Custom success page URL (gets ?redirectTo param)
debugLogs: false, // Enable server-side debug logging
}),
],
})| Option | Type | Default | Description |
|---|---|---|---|
| | required | Deep link scheme (without |
| | | Post-auth redirect path |
| | Generic message | Text shown on browser success page |
| | | Custom success page URL (receives |
| | | Log middleware activity to console |
/reset-password/callback/successappendCallbackURL()/sign-in/socialPlatformredirectURI?callbackURL=scheme://checkCallbackURL()callbackURLscheme://?redirectTo=/callback/success<script>window.location.href = '{deepLinkURL}'</script>@tauri-apps/plugin-http// lib/auth-client.ts
import { isTauri } from "@tauri-apps/api/core"
import { fetch as tauriFetch } from "@tauri-apps/plugin-http"
import { platform } from "@tauri-apps/plugin-os"
import { createAuthClient } from "better-auth/react" // or "better-auth/client"
export const authClient = createAuthClient({
fetchOptions: {
customFetchImpl: (...params) =>
isTauri() && platform() === "macos" && window.location.protocol === "tauri:"
? tauriFetch(...params)
: fetch(...params)
}
})import { setupBetterAuthTauri } from "@daveyplate/better-auth-tauri"
import { authClient } from "./lib/auth-client"
const cleanup = setupBetterAuthTauri({
authClient,
scheme: "my-app", // Must match server config
debugLogs: false,
mainWindowLabel: "main", // Tauri window label (default: "main")
onRequest: (href) => {
console.log("Processing auth callback:", href)
},
onSuccess: (callbackURL) => {
window.location.href = callbackURL || "/"
},
onError: (error) => {
// error: { status: number, statusText: string, code?: string, message?: string }
console.error("Auth failed:", error.status, error.statusText)
},
})
// Call cleanup() when done (e.g., component unmount)import { useBetterAuthTauri } from "@daveyplate/better-auth-tauri/react"
import { authClient } from "./lib/auth-client"
function App() {
useBetterAuthTauri({
authClient,
scheme: "my-app",
onSuccess: (callbackURL) => {
navigate(callbackURL || "/")
},
onError: (error) => {
toast.error(`Auth failed: ${error.statusText}`)
},
})
return <YourApp />
}<script>
import { onMount, onDestroy } from "svelte"
import { setupBetterAuthTauri } from "@daveyplate/better-auth-tauri"
import { authClient } from "./lib/auth-client"
import { goto } from "$app/navigation"
let cleanup
onMount(() => {
cleanup = setupBetterAuthTauri({
authClient,
scheme: "my-app",
onSuccess: (callbackURL) => goto(callbackURL || "/"),
onError: (error) => console.error("Auth error:", error),
})
})
onDestroy(() => cleanup?.())
</script>import { signInSocial } from "@daveyplate/better-auth-tauri"
import { authClient } from "./lib/auth-client"
<button onClick={async () => {
const { data, error } = await signInSocial({
authClient,
provider: "google", // Any configured social provider
})
if (error) console.error("Social sign-in error:", error)
}}>
Sign in with Google
</button>isTauri()PlatformdisableRedirect: trueopenUrl()fetchOptions.throw: truecallbackURLscheme@daveyplate/better-auth-tauri → setupBetterAuthTauri, signInSocial, handleAuthDeepLink
@daveyplate/better-auth-tauri/react → useBetterAuthTauri (React hook)
@daveyplate/better-auth-tauri/plugin → tauri (server plugin factory)tauri.conf.jsontauri()betterAuth()customFetchImplsetupBetterAuthTauri()useBetterAuthTaurisignInSocial()authClient.signIn.social()GET /tauri.conf.json@daveyplate/better-auth-uiBetterAuthError: Invalid base URL: tauri://localhostbetter-authbetter-auth-best-practicesbetter-auth-create-authbetter-auth-email-passwordbetter-auth-explain-errorbetter-auth-organizationbetter-auth-providersbetter-auth-securitybetter-auth-two-factorbetter-auth-tauri-pitfallssawy-better-auth-ui-tauri-repro