@dreamshive/better-auth-tauri (英语)
更好的认证 用于 陶里语Name 桌面应用程序。 OAuth 发生在用户的 系统浏览器,而不是嵌入式的网页视图——会话 cookie通过自定义的 URI 方案深层链接回放,应用程序中每一个向外的请求都是透明认证的.
镜像官员的架构 @better-auth/expo 软件包,为Tauri浏览器风格的网页视图所改编.
为什么要用这个
- 屏蔽嵌入式网页视图的 OAuth 提供者仍然工作 (谷歌声)
disallowed_useragent,企业SSO,即将被微软强制执行. - 用户重新使用其现有的浏览器会话 (原始内容存档于2017-10-21). – 没有从您应用程序内部重新登录github.com/google.com.
- 全地址栏 + 挂锁 ——用户在签入前可以验证提供者的域名.
- 同样更好的 Auth API 表面 你继续使用
authClient.signIn.social(...)财务报告和审定财务报表useSession()好像什么都没变一样.
如何运作
┌──────────────────┐ ┌────────────────┐ ┌──────────────┐
│ Tauri app │ │ System browser │ │ Auth service │
│ (WKWebView/ │ │ (Safari/Chrome)│ │ │
│ WebView2) │ │ │ │ │
└────────┬─────────┘ └────────┬───────┘ └──────┬───────┘
│ signIn.social() │ │
│ ─────────────────────────────┼───────────────────────>│
│ │ │
│ <── { url, disableRedirect } ──────────────────────────│
│ │ │
│ shell.open(url) │ │
│ ────────────────────────────>│ │
│ │ /tauri-authz-proxy │
│ │ ──────────────────────>│
│ │ Set-Cookie: state=... │
│ │ <──────────────────────│
│ │ │
│ │ github.com OAuth... │
│ │ <─redirect to callback─│
│ │ /callback/github │
│ │ ──────────────────────>│
│ │ │
│ │ <── 302 sokudo://?cookie=<session>
│ │ (after-hook appends cookie
│ │ to custom-scheme redirect)
│ │ │
│ <── OS routes sokudo:// ────│ │
│ │
│ store cookie in OS keychain │
│ inject as `x-tauri-cookie` header on future calls │
│ ─────────────────────────────────────────────────────>│
│ │
│ <── server plugin rewrites x-tauri-cookie → Cookie ───│
│ Better Auth validates, returns session │
有两件事值得呼吁:
x-tauri-cookie头部走私. 获取光谱标记Cookie作为 禁用页眉名称。网页视图默默地放弃设置的尝试。 我们把会话偷运出去x-tauri-cookie编辑 服务器 插件重写到Cookie在更好的Auth检查请求之前.disableRedirect: true自动注射. 更好的 Auth 的 Vue/ React 客户端通常导航window.location.href到 OAuth URL — 在浏览器中罚款, 在单一窗口的 Tauri app 中灾难性(网页视图与提供者的登录页面相接) 。 客户端插件注入disableRedirect: true输入/sign-in/social财务报告和审定财务报表/sign-in/oauth2请求让客户留在原地.- Cookie突变被连载. 反应 cookie, 深链接回调, 和注册共享一个本地突变队列 。 延迟回复可能只撤销它发送的会话,OAuth召回无法在明确退出后恢复会话.
安装
bun add @dreamshive/better-auth-tauri
# or: npm install / pnpm add / yarn add
服务器插件需要 better-auth 1.7.16 或以后(与 2026-11-03 安全修复) (中文(简体) ).
Peer Tauri插件( 仅安装在消费客户端的桌面应用中):
bun add @tauri-apps/plugin-shell @tauri-apps/plugin-deep-link
cd src-tauri
cargo add tauri-plugin-shell tauri-plugin-deep-link
如果您想要包含的焦点/在线管理器工作, 您还需要:
bun add @tauri-apps/api # onFocusChanged lives here
Tauri 配置
1. 登记你的URI计划
src-tauri/tauri.conf.json数字 :
{
"plugins": {
"deep-link": {
"desktop": {
"schemes": ["yourapp"]
}
}
}
}
2. 启动 Rust 插件
src-tauri/src/lib.rs数字 :
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_deep_link::init())
// ... your other plugins
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
3. 许可
src-tauri/capabilities/default.json数字 :
{
"permissions": [
"core:default",
"shell:allow-open",
"deep-link:default"
]
}
4. (高度建议) 添加 tauri-plugin-single-instance
没有这个,打开一个 yourapp:// 在 Tauri app 已经运行时, 从浏览器的深链接可能会生成第二个窗口, 而不是重用第一个窗口 。 安装 :
cd src-tauri
cargo add tauri-plugin-single-instance --features deep-link
登记 在此之前 任何其它插件在 lib.rs数字 :
use tauri::{Emitter, Manager};
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_single_instance::init(|app, argv, _cwd| {
if let Some(window) = app.get_webview_window("main") {
let _ = window.unminimize();
let _ = window.show();
let _ = window.set_focus();
}
for arg in argv.iter().skip(1) {
if arg.starts_with("yourapp://") {
let _ = app.emit("deep-link://new-url", vec![arg.clone()]);
}
}
}))
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_deep_link::init())
// ...
}
服务器设置( Better Auth 后端)
import { betterAuth } from "better-auth";
import { tauri } from "@dreamshive/better-auth-tauri";
export const auth = betterAuth({
// ... your existing config
trustedOrigins: ["yourapp://"],
plugins: [
tauri(),
// ... your other plugins
],
});
服务器插件 :
- 重映射
tauri-origin→origin因此 CSRF / 信任起源检查接受自定义方案. - 重写
x-tauri-cookie→Cookie因此客户端走私的会话饼干在Better Auth中可见. - 附录
?cookie=<Set-Cookie>用于 OAuth 重定向 URL , 瞄准自定义方案, 因此 Tauri app 可以连接 cookie 罐 . - 显示 a
/tauri-authorization-proxy植入 OAuth 的终点state在系统浏览器的罐子中饼干在重定向到提供者之前(防止回调状态不匹配).
代理是一个公开的端点,因此它只对一个真正的登录出产的请求采取行动. 它仅在配置于 HTTPS 的供应商的授权来源上重定向到 HTTPS URL socialProviders 或者说 genericOAuth,它只表示一个 state 此服务器已发布但尚未过期 。 其他的都一样 400 没有饼干 没有方向 SSO 身份提供者(/sign-in/sso)不通过代理路由.
如果您在 0.1.5 启用服务器插件前运行了版本, 请旋转 BETTER_AUTH_SECRET: 这些版本签名 state 它们在定点前的签名一直有效 直到秘密改变.
(简体中文)
您的认证服务器的 CORS 配置必须允许此插件发送的自定义头 :
cors({
origin: [/* your frontend origins */, "yourapp://"],
credentials: true,
allowedHeaders: [
"Content-Type",
"Authorization",
"Cookie",
"tauri-origin",
"x-tauri-cookie",
"x-skip-oauth-proxy",
],
})
客户端设置
import { createAuthClient } from "better-auth/vue"; // or /react, /solid, etc.
import { tauriClient } from "@dreamshive/better-auth-tauri/client";
import { fetch as tauriFetch } from "@tauri-apps/plugin-http";
import { isTauri } from "@tauri-apps/api/core";
export const authClient = createAuthClient({
baseURL: "https://auth.example.com",
fetchOptions: {
// Always use tauriFetch in Tauri — the webview's native fetch() drops
// our smuggled headers and hits CORS on custom schemes.
customFetchImpl: (...args) =>
isTauri() ? tauriFetch(...args) : fetch(...args),
},
plugins: [
tauriClient({
scheme: "yourapp", // must match tauri.conf.json
cookiePrefix: "yourapp", // must match your server's cookie prefix
storage: {
// See "Secure storage" below — localStorage is dev-only.
getItem: (k) => localStorage.getItem(k),
setItem: (k, v) => localStorage.setItem(k, v),
},
}),
],
});
然后通常使用更好的 Auth :
await authClient.signIn.social({
provider: "github",
callbackURL: "/", // auto-rewritten to yourapp:///
});
插件从那里取出 .
安全存储
localStorage 实值 不适合生产 ——您 Tauri 网页视图中的任何 XSS都可以直接读取.
生产,退后 storage 选项。 选项, 按工程学排名 :
备选办法A——OS键链(建议)
展览 keyring-rs 通过定制 Tauri 命令。 每个平台的本土秘密商店(Apple Keychain,Windows Creditic Manager,特务在Linux).
cd src-tauri
cargo add keyring --features apple-native,windows-native,linux-native-sync-persistent
// src-tauri/src/keyring.rs
use keyring::Entry;
#[tauri::command]
pub fn keyring_get(service: String, key: String) -> Result<Option<String>, String> {
let entry = Entry::new(&service, &key).map_err(|e| e.to_string())?;
match entry.get_password() {
Ok(v) => Ok(Some(v)),
Err(keyring::Error::NoEntry) => Ok(None),
Err(e) => Err(e.to_string()),
}
}
#[tauri::command]
pub fn keyring_set(service: String, key: String, value: String) -> Result<(), String> {
let entry = Entry::new(&service, &key).map_err(|e| e.to_string())?;
entry.set_password(&value).map_err(|e| e.to_string())
}
登记时间 lib.rs数字 :
mod keyring;
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
keyring::keyring_get,
keyring::keyring_set,
])
在您的客户端存储适配器中:
import { invoke } from "@tauri-apps/api/core";
const SERVICE = "com.yourapp.session";
const keyringStorage = {
getItem: (key: string) => invoke<string | null>("keyring_get", { service: SERVICE, key }),
setItem: (key: string, value: string) => invoke("keyring_set", { service: SERVICE, key, value }),
};
备选案文B - tauri-plugin-stronghold
官方Tauri加密金库(IOTA Stronghold). 需要应用程序提供的主密码句 .
备选案文C- @tauri-apps/plugin-store
上盘JSON,没有加密,而是住在Tauri IPC后方(无法从 document.localStorage) (中文(简体) ). 边际改善 localStorage。 。 。 .
选项
tauri(options?) — 服务器插件
| 选项 | 默认 | 说明 |
|---|---|---|
disableOriginOverride | false | 别乱画 tauri-origin → origin。 。 。 . |
tauriClient(options) ——客户端插件
| 选项 | 默认 | 说明 |
|---|---|---|
scheme | ——(需要) | 自定义 URI 方案注册于 tauri.conf.json。 。 。 . |
storage | ——(需要) | 密钥/值存储适配器. 支持自动同步方法 . |
storagePrefix | "better-auth" | 存储密钥的前缀 . |
cookiePrefix | "better-auth" | 服务器 cookie-name 前缀( s). |
disableCache | false | 禁用本地语言 /get-session 响应缓存. |
refetchOnWindowFocus | true | 当 Tauri 窗口重起焦点时, 反射会话 . |
refetchOnReconnect | true | 网络恢复上线时的回溯会话 . |
出口
// Server
import { tauri, tauriAuthorizationProxy } from "@dreamshive/better-auth-tauri";
// Client
import {
tauriClient,
setupTauriFocusManager, // manual wiring if you disabled auto-setup
setupTauriOnlineManager,
} from "@dreamshive/better-auth-tauri/client";
macOS dev 工作流程
MacOS上的深链接URI计划通过应用程序在发射服务注册 .app 捆绑 Info.plist。 。 。 . tauri dev 不产生捆绑 ——它运行一个生的二进制,macOS无法引导深层链接到.
针对macOS上深链插件开发的官方 Tauri 指导 :
# Build once (also run after any Rust/plugin/capability change)
bun tauri build --debug
# Open the bundle so Launch Services indexes it
open src-tauri/target/debug/bundle/macos/your-app.app
前端 (Vue/React/Svelte) HMR 可以通过已建成的应用程序运行, 如果您指向的话 frontendDist 在您的 dev 服务器上:
// src-tauri/tauri.dev.conf.json
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"build": {
"frontendDist": "http://localhost:3000",
"beforeBuildCommand": ""
}
}
bun tauri build --debug --config src-tauri/tauri.dev.conf.json
已知的限制
- macOS dev 循环需要捆绑 (见上文)。 Windows和Linux没有这种摩擦.
- 劫持计划 ——任何应用程序都可以注册为您的自定义计划的操作员. 具有有意义的攻击表面的生产应用软件应当考虑通用链接(macOS)或App链接(Android等同).
- 存储是呼叫者的责任. 此插件不会捆绑一个安全的存储原始; 您提供适配器 .
- 没有安保部门支助. Tauri apps 仅限客户端.
与 @better-auth/expo
Authority流量的地物对地平价,外加两个Tauri特异性加法:
| 特性 | 博览 | Tauri( 这个) |
|---|---|---|
| OAuth 通过系统浏览器 | ✅ | ✅ |
| Cookie 桥通过 URL 查询参数 | ✅ | ✅ |
| 状态的授权代理端点 | ✅ | ✅ |
| 重定向到策略调用重写 | ✅ | ✅ |
| 注册本地清理 | ✅ | ✅ |
| 第三方饼干过滤器 | ✅ | ✅ |
| 焦点调试管理器 | QQ (响应查询) | QQ (通知更好的 Auth 会话信号) |
| 在线 reach 管理器 | QQ (响应查询) | QQ (通知更好的 Auth 会话信号) |
x-tauri-cookie 走私 | —— 说 | 需要( 浏览器禁止 Cookie(中文(简体) ) |
disableRedirect: true 注射 | —— 说 | 需要(浏览器有) window.location(中文(简体) ) |
许可证
麻省理工学院 鲁利·阿尔迪亚斯亚