前言
在 Tauri 中实现 OAuth 登录,需要结合前端与后端的功能,通过 WebView 与系统浏览器实现安全的用户认证。本文将介绍两种实现方式,并重点解析通过 App 内部监听的方式实现 OAuth 登录的完整流程。
实现思路
1. 通过浏览器唤起桌面应用
- 用户点击登录按钮,Tauri 打开默认浏览器访问 OAuth 服务端的授权 URL。
- 用户完成授权后,服务端通过回调 URL 将授权码(
code)返回。 - 应用从回调中获取
code,并通过 API 请求换取access_token。 - 保存用户信息和
access_token,完成登录。
2. App 内部监听
- 用户点击登录按钮,Tauri 打开默认浏览器访问 OAuth 服务端的授权 URL。
- 用户完成授权后,服务端通过回调 URL 将授权码(
code)返回。 - 应用通过监听功能从本地端口中获取
code,并通过 API 请求换取access_token。 - 保存用户信息和
access_token,完成登录。
区别
第一种方式通过回调获取参数,第二种方式通过 App 内部监听完成。本文将重点介绍第二种方式。
OAuth 登录流程
接下来以 GitHub OAuth 为例,讲解如何实现完整的 OAuth 登录。
1. Tauri APP 前端实现
登录按钮与授权逻辑
tsx代码解读复制代码import { shell } from "@tauri-apps/api"; import { listen } from "@tauri-apps/api/event"; import { invoke } from "@tauri-apps/api/tauri"; import { getCurrentWindow } from "@tauri-apps/api/window"; async function signIn() { let resolvePromise: (url: URL) => void; try { // 开启事件监听 const stopListening = await listen("oauth://url", (data: { payload: string }) => { const urlObject = new URL(data.payload); resolvePromise(urlObject); }); // 停止可能存在的 OAuth 服务 try { await invoke("plugin:oauth|stop"); } catch (e) { // 忽略错误 } // 启动 OAuth 服务 const port: string = await invoke("plugin:oauth|start", { config: { response: callbackTemplate, headers: { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store, no-cache, must-revalidate", Pragma: "no-cache", }, cleanup: true, }, }); const uid = uuidv4(); // 生成业务需求的唯一 ID await shell.open(`http://localhost:1420/login?provider=coco-cloud&request_id=${uid}&port=${port}`); // 获取监听回调的 URL const url = await new Promise<URL>((r) => { resolvePromise = r; }); stopListening(); // 获取回调的 code 参数 const code = url.searchParams.get("code"); if (!code) throw new Error("Invalid token or expired"); // 请求后端接口进行认证 const response: any = await tauriFetch({ url: `/auth/request_access_token?request_id=${uid}`, method: "GET", headers: { "X-API-TOKEN": code }, }); await setAuth({ token: response.access_token, expires: response.expire_at }); await getCurrentWindow().setFocus(); } catch (error) { console.error("Sign in failed:", error); await setAuth(undefined); throw error; } } // 监听登录状态变化 useEffect(() => { const setupAuthListener = async () => { if (!auth) { // navigate("/signin", { replace: true }); } }; setupAuthListener(); return () => { invoke("plugin:oauth|stop").catch(() => {}); }; }, [auth]);
2. 配置 OAuth 提供商
以 GitHub OAuth 为例:
-
登录 GitHub 开发者平台,创建 OAuth 应用。
-
配置:
- 回调 URL: 例如
http://localhost:1420/auth/callback - 获取
client_id和client_secret
- 回调 URL: 例如
3. Web 浏览器端实现
授权页面逻辑
ini代码解读复制代码import { useSearchParams } from "react-router-dom"; const [searchParams] = useSearchParams(); const uid = searchParams.get("request_id"); const port = searchParams.get("port"); const authWithGithub = (uid: string, port: string) => { const authorizeUrl = "https://github.com/login/oauth/authorize"; window.location.href = `${authorizeUrl}?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost:1420/login?port=${port}`; }; function handleGithubSignIn() { uid && authWithGithub(uid, port!); } // 回调处理 useEffect(() => { const code = searchParams.get("code"); if (code) { setTimeout(() => { window.location.href = `http://localhost:${port}/?code=${code}&provider=coco-cloud`; }, 1000); } }, []);
小结
通过 Tauri 的监听功能与 Web 浏览器的联动,可以实现 OAuth 登录的完整流程。关键点包括:
- 启动本地监听服务并生成唯一标识。
- 跳转浏览器完成用户授权。
- 通过监听获取授权结果,完成登录。
这种实现方式简洁灵活,适用于多种 OAuth 场景。
开源
最近,我正在基于 Tauri 开发一款项目,名为 Coco。目前已开源,项目仍在不断完善中,欢迎大家前往支持并为项目点亮免费的 star 🌟!
作为个人的第一个 Tauri 项目,开发过程中也是边探索边学习。希望能与志同道合的朋友一起交流经验、共同成长!
代码中如有问题或不足之处,期待小伙伴们的宝贵建议和指导!
- 官网: coco.rs/
- 前端仓库: github.com/infinilabs/…
- 服务端仓库: github.com/infinilabs/…
非常感谢您的支持与关注!