A minimalistic Rust library and Tauri plugin for handling browser-based OAuth flows in desktop applications. This plugin spawns a temporary localhost server to capture OAuth redirects, solving the challenge of using OAuth with desktop apps.
Many OAuth providers (like Google and GitHub) don't allow custom URI schemes ("deep links") as redirect URLs. This plugin provides a solution by:
- Spawning a temporary local server
- Capturing the OAuth redirect
- Passing the authorization data back to your app
Note: For an alternative approach using deep linking, see tauri-plugin-deep-link. The deep-link plugin can automatically start your app if there's no open instance.
# Cargo.toml
[dependencies]
tauri-plugin-oauth = "2"For Tauri projects using npm or yarn:
npm install @fabianlars/tauri-plugin-oauth@2
# or
yarn add @fabianlars/tauri-plugin-oauth@2use tauri::{command, Emitter, Window};
use tauri_plugin_oauth::start;
#[command]
async fn start_server(window: Window) -> Result<u16, String> {
start(move |url| {
// Because of the unprotected localhost port, you must verify the URL here.
// Preferebly send back only the token, or nothing at all if you can handle everything else in Rust.
let _ = window.emit("redirect_uri", url);
})
.map_err(|err| err.to_string())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_oauth::init())
.invoke_handler(tauri::generate_handler![start_server])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}import {
start,
cancel,
onUrl,
onInvalidUrl,
} from "@fabianlars/tauri-plugin-oauth";
async function startOAuthFlow() {
try {
const port = await start();
console.log(`OAuth server started on port ${port}`);
// Set up listeners for OAuth results
await onUrl((url) => {
console.log("Received OAuth URL:", url);
// Handle the OAuth redirect
});
// Initiate your OAuth flow here
// ...
} catch (error) {
console.error("Error starting OAuth server:", error);
}
}
// Don't forget to stop the server when you're done
async function stopOAuthServer() {
try {
await cancel(port);
console.log("OAuth server stopped");
} catch (error) {
console.error("Error stopping OAuth server:", error);
}
}You can configure the plugin behavior using the OauthConfig struct:
use tauri_plugin_oauth::OauthConfig;
let config = OauthConfig {
ports: Some(vec![8000, 8001, 8002]),
response: Some("OAuth process completed. You can close this window.".into()),
..Default::default()
};
start_with_config(config, |url| {
// Handle OAuth URL
})
.expect("Failed to start OAuth server");If you set the redirect_uri field, the plugin will respond to the OAuth callback with a
302 Found to that URL instead of serving an HTML response. This is useful when you want the
browser to land on a real page of your app (e.g. a Tauri window URL) once the provider has hit
the localhost server.
let config = OauthConfig {
redirect_uri: Some("http://tauri.localhost/oauth/done".into()),
..Default::default()
};Note: When
redirect_uriis set, the handler closure receives the localhost URL the provider hit (e.g.http://127.0.0.1:<port>/?code=...&state=...) — not the full URL with the fragment (#...) that only the browser sees. If your OAuth flow returns the token in a URL fragment (implicit flow), parsewindow.locationon the redirected page and forward the result back to your app yourself; the default (noredirect_uri) behavior uses an inline script that already does this for you.
- Always validate the received OAuth URL on your server-side before considering it authentic.
- Use HTTPS for your OAuth flow to prevent man-in-the-middle attacks.
- Implement proper token storage and refresh mechanisms in your application.
Contributions are always welcome! Please feel free to submit a Pull Request.
This project is dual-licensed under either of the following licenses, at your option: