tauri-plugin-oauth

A Tauri plugin that spawns a temporary localhost server to capture browser-based OAuth redirects in desktop apps.

Library
Cargo
v2.1.0
214stars
MIT OR Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
61/100Good
Development Activity64
Maintenance40
Community64
Maturity56
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
66/100Good
Architecture68
Code Quality55
Innovation62
Learning Curve80

tauri-plugin-oauth solves a specific problem for Tauri desktop applications: many OAuth providers (Google, GitHub, X, and others) refuse to accept custom URI schemes as redirect URLs, which is the usual way a native app receives an OAuth callback. Instead of relying on deep links, this plugin binds a short-lived TCP listener on 127.0.0.1, hands the OAuth provider a normal http://127.0.0.1:<port> redirect target, and forwards whatever URL the provider hits back into the Rust side of the app as an event.

The crate ships both a plain Rust library (start/start_with_config/cancel) usable outside of Tauri’s command system, and a Tauri plugin wrapper that exposes start/cancel as invokable commands with a matching TypeScript client (@fabianlars/tauri-plugin-oauth) for the frontend. Configuration covers fixed vs. OS-assigned ports, a custom HTML response served to the browser after the redirect, and an optional redirect_uri that issues a 302 instead of an inline page — useful when the app wants the browser to land on one of its own hosted routes once the callback fires.

Because the listener binds an unauthenticated localhost port, the library is intentionally minimal and pushes URL verification back onto the consuming application rather than trying to guess what’s safe.

What You Get

  • A start/start_with_config Rust API that binds a localhost TCP listener and returns the port it is listening on
  • A Tauri plugin (init()) exposing start and cancel as JS-invokable commands with an event bridge (oauth://url, oauth://invalid-url)
  • A TypeScript client package (@fabianlars/tauri-plugin-oauth) with start, cancel, onUrl, and onInvalidUrl helpers
  • Configurable behavior: fixed candidate ports, a custom HTML response page, or a redirect_uri that 302s the browser to an app-hosted page instead
  • An inline script fallback that resolves the browser’s full URL (including hash fragments) back to the local server via a Full-Url header fetch

Common Use Cases

  • Adding ‘Login with GitHub/Google/X’ to a Tauri desktop app whose OAuth provider rejects custom URI schemes
  • Capturing an OAuth authorization code or token server-side in Rust without exposing it to the app’s webview directly
  • Redirecting the user’s browser back to a real hosted page after the OAuth callback via the redirect_uri option
  • Building a Rust-only (non-Tauri) CLI or desktop tool that still needs a local OAuth redirect catcher, using the plugin’s underlying library functions directly

Under The Hood

Architecture Single-file plugin crate (src/lib.rs, ~280 lines) built around a blocking TCP listener spawned on its own thread; start_with_config binds a TcpListener, spawns a thread that loops over listener.incoming(), and hands each connection to handle_connection, which does hand-rolled HTTP parsing via httparse to extract the path and a custom Full-Url header, replying with either a raw 200 HTML page (with an injected script that fetches the browser’s full URL back to the server for fragment-token recovery) or a 302 to a configured redirect_uri. The plugin_impl inner module is a thin Tauri-command wrapper around the same start_with_config/cancel functions, forwarding results as oauth://url / oauth://invalid-url window events. Shutdown is cooperative: a caller can call cancel(port) (opens a TCP connection and writes a 4-byte EXIT sentinel) or hit /exit over HTTP, either of which breaks the accept loop. There’s no framework abstraction beyond this — a single thread, single accept loop, one connection handled at a time — which keeps the surface small but means a flow that never completes leaves the thread parked.

Tech Stack Cargo.toml pulls tauri = "2" (plugin host), httparse = "1" for zero-copy HTTP request parsing, url = "2" for validating the returned redirect, log = "0.4" for tracing, serde = "1" for deserializing OauthConfig from the Tauri plugin config, and thiserror = "2" for error typing though lib.rs mostly returns std::io::Error directly today. build.rs uses tauri-plugin (build feature) to declare the exposed commands (start, cancel) and generate the ACL/permission scaffolding under permissions/. The JS side is a small Rollup-built TypeScript package (@fabianlars/tauri-plugin-oauth) wrapping @tauri-apps/api’s invoke/listen, versioned alongside the crate. An example Tauri app under examples/vanilla exercises the plugin end-to-end with a plain HTML/TS frontend and its own src-tauri.

Code Quality No test files exist anywhere in the repository — no #[cfg(test)] modules, no tests/ directory, and the npm pretest script just runs the build rather than a test runner — so the HTTP parsing and redirect logic have no automated coverage. Error handling is present but coarse: handle_connection logs and returns None on write/flush failures rather than propagating a typed error, and the fixed 4048-byte read buffer is compared via an unchecked buffer[..4] == EXIT slice that would panic on a short read. CI (lint.yml, format.yml) runs cargo clippy and formatting checks for both Rust and JS, but there is no cargo test or build-verification job. The public API surface (start, start_with_config, cancel, OauthConfig) is small, named clearly, and documented with doc comments, but the absence of tests is a real gap for a security-adjacent local server.

API Design The API is intentionally minimal — one function to start (with or without config), one to cancel, and a config struct of three optional fields. Rust callers get a plain closure-based callback; JS callers get an async start()/cancel() pair plus onUrl/onInvalidUrl event subscriptions mirroring Tauri’s usual invoke/listen idiom. The library deliberately punts on convenience work: it does not verify the OAuth response’s trustworthiness or parse query params, and both code comments and the README repeatedly flag that callers must validate the URL themselves — a scope boundary rather than an oversight. That keeps the dependency footprint and attack surface small at the cost of leaving state/PKCE validation entirely to the consumer.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search