Introduction
Implementing OAuth login in Tauri requires integrating both frontend and backend functionalities. It uses WebView and the system browser to securely authenticate users. This article outlines a complete implementation process with key steps.
Implementation Overview
1. Launching Desktop Applications via Browser
- The user clicks the login button, and Tauri opens the default browser to access the OAuth server's authorization URL.
- After completing the authorization, the server returns an authorization code (
code
) via a callback URL. - The application retrieves the
code
from the callback and exchanges it for anaccess_token
via an API request. - User information and
access_token
are stored, completing the login process.
2. Internal App Listening
- The user clicks the login button, and Tauri opens the default browser to access the OAuth server's authorization URL.
- After completing the authorization, the server returns an authorization code (
code
). - The application retrieves the
code
through an internal listener and exchanges it for anaccess_token
via an API request. - User information and
access_token
are stored, completing the login process.
The difference between these two approaches lies in how the code
is obtained: the first method retrieves it from the callback, while the second listens for it internally within the app.
This article focuses on the second approach.
OAuth Login Workflow
The following example demonstrates the implementation process using GitHub OAuth.
Tauri App Frontend Implementation
- Login Button Event
async function signIn() {
let res: (url: URL) => void;
try {
const stopListening = await listen(
"oauth://url",
(data: { payload: string }) => {
console.log(111, data.payload);
const urlObject = new URL(data.payload);
res(urlObject);
}
);
try {
await invoke("plugin:oauth|stop");
} catch (e) {
// Ignore errors if no server is running
}
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,
},
});
let uid = app_uid;
if (!uid) {
uid = uuidv4();
setAppUid(uid);
}
await shell.open(
`http://localhost:1420/login?provider=coco-cloud&product=coco&request_id=${uid}&port=${port}`
);
const url = await new Promise<URL>((r) => {
res = r;
});
stopListening();
const code = url.searchParams.get("code");
const provider = url.searchParams.get("provider");
if (!code || provider !== "coco-cloud") {
throw new Error("Invalid token or expires");
}
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,
plan: { upgraded: false, last_checked: 0 },
});
getCurrentWindow()
.setFocus()
.catch(() => {});
} catch (error) {
console.error("Sign in failed:", error);
await setAuth(undefined);
throw error;
}
}
// Listen for auth status
useEffect(() => {
const setupAuthListener = async () => {
try {
if (!auth) {
// Redirect to the signin page
}
} catch (error) {
console.error("Failed to set up auth listener:", error);
}
};
setupAuthListener();
return () => {
const cleanup = async () => {
try {
await invoke("plugin:oauth|stop");
} catch (e) {}
};
cleanup();
};
}, [auth]);
Configuring OAuth Provider
For GitHub OAuth:
- Log in to the GitHub Developer platform and create an OAuth application.
- Configure:
- Callback URL: For example,
http://localhost:1420/auth/callback
- Retrieve
client_id
andclient_secret
.
- Callback URL: For example,
Web Browser Implementation
import { useSearchParams } from "react-router-dom";
const [searchParams] = useSearchParams();
const uid = searchParams.get("request_id");
const port = searchParams.get("port");
const code = searchParams.get("code");
// GitHub Authorization
const authWithGithub = (uid: string, port: any) => {
const authorizeUrl = "https://github.com/login/oauth/authorize";
location.href = `${authorizeUrl}?client_id=${"xxxxxxxx"}&redirect_uri=http://localhost:1420/login?port=${port}`;
};
// GitHub Login
function handleGithubSignIn() {
uid && authWithGithub(uid, port);
}
// Monitor code and callback to app
useEffect(() => {
setTimeout(() => {
window.location.href = `http://localhost:${port}/?code=${code}&provider=coco-cloud`;
}, 10000);
}, [code]);
Conclusion
This guide demonstrates how to implement OAuth login in a Tauri app using GitHub OAuth. It covers both frontend and backend integration, providing a secure and user-friendly login experience. With this method, developers can adapt the approach to other OAuth providers as needed.
Open Source
Recently, I’ve been working on a project based on Tauri called Coco. It’s open source and under continuous improvement. I’d love your support—please give the project a free star 🌟!
This is my first Tauri project, and I’ve been learning while exploring. I look forward to connecting with like-minded individuals to share experiences and grow together!
- Official website: coco.rs/
- Frontend repo: github.com/infinilabs/coco-app
- Backend repo: github.com/infinilabs/coco-server
Thank you for your support and attention!
Source: View source