Tauri
gau
has first-class Tauri support. This guide explains how to build desktop apps with OAuth, and also shows how to set up the required deep linking to handle OAuth redirects.
This guide assumes you have already completed the setup for your web framework (e.g., SvelteKit).
-
Initialize Tauri
Section titled “Initialize Tauri”Initialize Tauri in your project. This will create a
src-tauri
directory.Terminal window npx tauri initTerminal window pnpm tauri initTerminal window yarn tauri initTerminal window bun tauri initTerminal window nlx tauri initMake sure you have these installed:
Terminal window npm i @tauri-apps/apinpm i -D @tauri-apps/cliTerminal window pnpm add @tauri-apps/apipnpm add -D @tauri-apps/cliTerminal window yarn add @tauri-apps/apiyarn add -D @tauri-apps/cliTerminal window bun add @tauri-apps/apibun add -D @tauri-apps/cliTerminal window ni @tauri-apps/apini -D @tauri-apps/cliAlso set up the
build
config intauri.conf.json
, and follow other Tauri setup steps specific to your frontend framework. -
Install Tauri Plugins
Section titled “Install Tauri Plugins”gau
requires the following plugins:Terminal window npm run tauri add osnpm run tauri add shellnpm run tauri add deep-linkTerminal window pnpm tauri add ospnpm tauri add shellpnpm tauri add deep-linkTerminal window yarn run tauri add osyarn run tauri add shellyarn run tauri add deep-linkTerminal window bun tauri add osbun tauri add shellbun tauri add deep-linkTerminal window nlx tauri add osnlx tauri add shellnlx tauri add deep-linkAnd add the
single-instance
plugin to yoursrc-tauri/Cargo.toml
.src-tauri/Cargo.toml [dependencies]# ...[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\"))".dependencies]tauri-plugin-single-instance = { version = "2.0.0", features = [ "deep-link" ] } -
Configure Deep Linking
Section titled “Configure Deep Linking”To handle OAuth callbacks, you need to configure a custom URL scheme. After the auth flow is done, the browser will open your app again with the URL you set (
gau://...
).In
src-tauri/tauri.conf.json
, add thedeep-link
plugin and define a scheme. Set this to something unique to your app.src-tauri/tauri.conf.json {// ..."plugins": {"deep-link": {"desktop": {"schemes": ["gau"]}}}}You need to configure
gau
to use this scheme:src/lib/auth.svelte.ts import { createSvelteAuth } from '@rttnd/gau/client/svelte'export const auth = createSvelteAuth({scheme: 'gau',})Add the
core:event:default
permission to thesrc-tauri/capabilities/default.json
file.src-tauri/capabilities/default.json {// ..."permissions": ["core:default","core:event:default","os:default","shell:default","deep-link:default"]} -
Set up Rust Backend
Section titled “Set up Rust Backend”Here is a working
src-tauri/src/lib.rs
to initialize the deep link plugin and listen for incoming URLs. The listener forwards the URL to the frontend via an event.src-tauri/src/lib.rs use tauri::{Manager, Emitter};use tauri_plugin_deep_link::DeepLinkExt;#[cfg_attr(mobile, tauri::mobile_entry_point)]pub fn run() {let mut builder = tauri::Builder::default();#[cfg(desktop)]{builder = builder.plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| {if let Some(window) = app.get_webview_window("main") {let _ = window.unminimize();let _ = window.set_focus();}},));}builder.plugin(tauri_plugin_deep_link::init()).plugin(tauri_plugin_os::init()).plugin(tauri_plugin_shell::init()).setup(|app| {#[cfg(any(windows, target_os = "linux"))]#[cfg(debug_assertions)]{use tauri_plugin_deep_link::DeepLinkExt;let _ = app.deep_link().register_all();}let handle = app.handle().clone();let handle_for_closure = handle.clone();handle.deep_link().on_open_url(move |event| {if let Some(window) = handle_for_closure.get_webview_window("main") {if let Some(url) = event.urls().first() {let _ = window.emit("deep-link", url.to_string());}}});Ok(())}).run(tauri::generate_context!()).expect("error while running tauri application");} -
Configure the Frontend
Section titled “Configure the Frontend”The frontend needs to be configured to build as a static single-page app for Tauri, listen for the deep link event, and handle the sign-in flow. If you are building a full-stack app, you can still host your backend, and the Tauri app will communicate with it as well.
First, update
svelte.config.js
to use@sveltejs/adapter-static
when building for Tauri.svelte.config.js import process from 'node:process'import Auto from '@sveltejs/adapter-auto'import Static from '@sveltejs/adapter-static'import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'const isTauri = !!process.env.TAURI_ENV_PLATFORM/** @type {import('@sveltejs/kit').Config} */const config = {preprocess: vitePreprocess(),kit: {adapter: isTauri? Static({ fallback: 'index.html' }): Auto(),},}export default configConfigure Vite to use a different port for Tauri. This way you can run your app on
localhost:5173
(which serves as the backend) and the Tauri app onlocalhost:4173
.vite.config.ts import process from 'node:process'import { sveltekit } from '@sveltejs/kit/vite'import { defineConfig } from 'vite'const isTauri = !!process.env.TAURI_ENV_PLATFORMexport default defineConfig({plugins: [sveltekit()],server: {port: isTauri ? 4173 : 5173,strictPort: true,},})Your Tauri development URL in
tauri.conf.json
should match this port (e.g.,http://localhost:4173
).Create a root layout that disables SSR, as Tauri apps are client-side rendered.
src/routes/+layout.ts export const ssr = falseThe
@rttnd/gau/client/svelte
package automatically handles the Tauri-specific logic. Yourauth.svelte.ts
file remains the same.Remember that your Tauri app is a static frontend and needs to communicate with your deployed backend.
src/lib/auth.svelte.ts import { PUBLIC_API_URL } from '$env/static/public'import { createSvelteAuth } from '@rttnd/gau/client/svelte'export const auth = createSvelteAuth({scheme: 'gau',baseUrl: PUBLIC_API_URL, // for dev, use http://localhost:5173/api/auth})Finally, you can use the
auth
store in your components. ThesignIn
method will automatically open the browser for OAuth and the app will receive the token via the deep link.src/routes/+page.svelte <script>import { auth } from '$lib/auth.svelte'</script>{#if auth.session?.user}<p>Welcome, {auth.session.user.name}!</p><button onclick={() => auth.signOut()}>Sign Out</button>{:else}<button onclick={() => auth.signIn('github')}>Sign in with GitHub</button>{/if}Coming soon.
-
Configure the Backend
Section titled “Configure the Backend”When using Tauri, the request to your authentication backend might not have a standard
Origin
header.gau
’s CSRF protection checks this header forPOST
requests. You should configuretrustHosts
in yourcreateAuth
options to allow requests from your Tauri app.src/lib/server/auth.ts import { createAuth } from '@rttnd/gau/core'export const auth = createAuth({// ... other optionstrustHosts: ['tauri.localhost']})
Your application is now configured for desktop authentication with Tauri! When a user clicks a sign-in button, gau
will open the system browser for the OAuth flow. After authorization, the provider redirects to a page that triggers the gau://
deep link, sending the session token back to your Tauri app securely.
This setup allows you to deploy the app on the web and build a desktop app too, with the same codebase.