Understanding Agx and Its Constraints
At Agnostic, we’ve crafted agx as a modern desktop application powered by Tauri, SvelteKit, and Plot, with ClickHouse driving fast local data processing. Our aim is to give users an intuitive interface to explore and query data effortlessly. What makes agx unique is its ability to operate in two modes:
- Desktop Mode: A native app using ClickHouse for fast, local data queries.
- Web Mode: A browser-based interface connecting to a remote server instance.
Delivering a seamless experience across both modes is a challenge, particularly for authentication, which must work smoothly in both native and web environments while prioritizing security and ease of use. For the desktop side, we tap into Tauri’s deep-linking
and opener
plugins to manage Auth0’s Universal Login flow, using a custom agx://localhost/auth
protocol to redirect back to the app.
Why Auth0? The Need for Authentication
To unlock advanced features like agx’s AI Assistant, users need to log in. We chose Auth0 with its Universal Login flow for its:
- Seamless SSO: Universal Login provides a unified, hosted login experience across web and desktop.
- Cross-Platform Compatibility: Works with SvelteKit’s frontend and Tauri’s Rust-based backend.
- Security: Leverages OAuth 2.0, JWT, and secure token storage.
- Ease of Use: The hosted Universal Login page is easy to set up and customize, letting us align it with agx’s branding.
By combining Auth0 with Tauri’s plugins, we ensure users enjoy a consistent login experience, whether running agx natively or in a browser, unlocking the AI Assistant with minimal hassle.
Configuring Auth0 with Universal Login Step-by-Step
To integrate Auth0’s SPA application type with Universal Login, configure the Auth0 dashboard and your Tauri + SvelteKit project as follows:
Create an Auth0 Application
Start by heading to the Auth0 dashboard and creating a new application. Select Single Page Application (SPA), as it aligns perfectly with SvelteKit’s architecture and Tauri’s webview.
Configure Application Settings
To enable secure redirects for both desktop and web modes, we configure Auth0’s URLs to support the deep-linking
plugin’s custom protocol and standard web callbacks.
In the application settings:
- Set Allowed Callback URLs to:
-
agx://localhost/auth
for the desktop app. This custom protocol is registered by thedeep-linking
plugin, allowing Auth0 to redirect back to the Tauri app after login. -
http://localhost:1420
for web development (agx’s default development port). - The production URL
https://agx.app
for web mode.
-
- Set Allowed Logout URLs to
http://localhost:1420
,https://agx.app
andagx://localhost/auth
. - Add Web Origins for:
-
http://localhost:1420
and the production domainhttps://agx.app
for web mode. -
tauri://localhost
andhttps://tauri.localhost
for the desktop app. These are necessary because Tauri’s webview sets the request origin totauri://localhost
orhttps://tauri.localhost
, and Auth0 requires these origins to be explicitly allowed to prevent CORS errors during authentication.
-
The agx://localhost/auth
URI must be added to Allowed Callback URLs to enable the deep-linking
plugin to handle redirects back to the desktop app. Similarly, tauri://localhost
and https://tauri.localhost
in Web Origins ensure Auth0 recognizes the Tauri app’s origin for secure authentication.
Set Up Tauri Plugins
To prepare Tauri, install the deep-linking
and opener
plugins using the Tauri CLI with these commands:
$ npm run tauri add deep-linking
$ npm run tauri add opener
Run one command at a time to avoid issues. If agx is well-configured, the Tauri CLI will automatically add the necessary permissions and plugins to the app builder. If something goes wrong, check the requirements on the plugins’ documentation pages to resolve any hiccups.
In tauri.conf.json
, configure the deep-linking plugin to register the agx://
protocol for desktop redirects.
{
"plugins": {
"deep-linking": {
"desktop": {
"schemes": ["agx"]
}
}
}
}
Finally, add the Auth0 SPA SDK to the SvelteKit project with npm install @auth0/auth0-spa-js
. These steps lay the foundation for Auth0’s Universal Login to work seamlessly across agx’s web and desktop modes, with the deep-linking
plugin managing desktop redirects and the opener
plugin handling browser-based logins.
Wiring Up Auth0: The Code
Here’s how we at Agnostic integrated Auth0’s SPA SDK with Universal Login, using Tauri’s deep-linking
and opener
plugins. To make the deep-linking
plugin work for handling agx://localhost/auth
redirects in desktop mode, build the app with npm run tauri build
to create a production bundle, and add the --debug
flag to enable devtools for easier debugging. The code below uses environment variables for Auth0 configuration, checks platform-specific login states, and manages redirects dynamically for both web and native modes.
// lib/auth.ts
import { type Auth0Client, createAuth0Client } from "@auth0/auth0-spa-js";
import { onOpenUrl } from "@tauri-apps/plugin-deep-link";
import { openUrl } from "@tauri-apps/plugin-opener";
if (!AUTH0_DOMAIN) throw new Error("AUTH0_DOMAIN is not defined");
if (!AUTH0_CLIENT_ID) throw new Error("AUTH0_CLIENT_ID is not defined");
let client: Auth0Client;
async function init() {
client = await createAuth0Client({
domain: AUTH0_DOMAIN,
clientId: AUTH0_CLIENT_ID,
authorizationParams: {
redirect_uri: AUTH0_REDIRECT_URI || window.location.origin,
},
cacheLocation: "localstorage",
useRefreshTokens: true,
});
}
export async function checkLoginState() {
await init();
if (PLATFORM === "WEB") {
if (
window.location.search.includes("code=") &&
window.location.search.includes("state=")
) {
await client.handleRedirectCallback();
window.history.replaceState({}, document.title, "/");
}
}
if (PLATFORM === "NATIVE") {
await onOpenUrl(async (urls) => {
const url = urls
.map((u) => new URL(u))
.find((u) => u.searchParams.has("code") && u.searchParams.has("state"));
if (url) {
await client.handleRedirectCallback(url.toString());
}
});
}
}
export async function isAuthenticated() {
if (client) {
return await client.isAuthenticated();
}
return false;
}
export async function getToken() {
if (client) return await client.getTokenSilently();
}
export async function login() {
if (client) {
await client.loginWithRedirect({
async openUrl(url) {
if (PLATFORM === "WEB") {
window.location.assign(url);
}
if (PLATFORM === "NATIVE") {
await openUrl(url);
}
},
});
}
}
export async function logout() {
if (client) {
await client.logout({
logoutParams: { returnTo: AUTH0_REDIRECT_URI || window.location.origin },
});
}
}
This code relies on environment variables (AUTH0_DOMAIN
, AUTH0_CLIENT_ID
, AUTH0_REDIRECT_URI
) to configure Auth0. The PLATFORM
constant (WEB
or NATIVE
) determines whether to handle redirects via the browser or the deep-linking
plugin’s onOpenUrl
. For desktop mode, the opener
plugin opens the Universal Login page in the default browser, and agx://localhost/auth
serves as the redirect URI, caught by the deep-linking
plugin. For web mode, it falls back to standard redirects using window.location.origin
. The useRefreshTokens: true
setting ensures secure token management for the SPA with Universal Login. The checkLoginState
function handles callback logic and cleaning up the URL after authentication.
Wrapping Up
At Agnostic, we’ve integrated Auth0’s Universal Login with Tauri’s deep-linking
and opener
plugins to deliver secure, seamless authentication for agx across web and desktop. The deep-linking
plugin handles redirects to agx://localhost/auth
, while the opener
plugin opens the Universal Login page in the default browser for desktop users. Including tauri://localhost
and https://tauri.localhost
as Web Origins in Auth0 eliminates CORS issues in Tauri’s webview. This setup creates a consistent experience, unlocking features like the AI Assistant. Want to dive into this implementation? Check out the agx
repository and give it a try!
Top comments (3)
I love how you handled deep-linking and CORS between web and desktop - so many teams trip on these details. Did you run into any gotchas with the Tauri plugins or discover any quirks you wish you'd known sooner?
Thanks for your comment! I’m glad you enjoyed the deep-linking and CORS approach—it was a fun challenge for agx’s web and desktop modes. With the Tauri plugins like deep-link and opener, I did hit a few gotchas. One thing I wish I’d known sooner was the need to explicitly add
tauri://localhost
andhttps://tauri.localhost
as Web Origins in Auth0—missing that caused some CORS headaches early on until I figured out Tauri’s webview sets those origins by default.Really clear thank you very much, i will try it