feat(desktop): intercept OAuth redirects to hosted frontend via webRequest

This commit is contained in:
CREDO23 2026-03-17 18:56:33 +02:00
parent a866e6caa1
commit d4ad5c7fe4
3 changed files with 15 additions and 19 deletions

View file

@ -1,4 +1,4 @@
import { app, BrowserWindow, shell, ipcMain } from 'electron'; import { app, BrowserWindow, shell, ipcMain, session } from 'electron';
import path from 'path'; import path from 'path';
import { spawn, ChildProcess } from 'child_process'; import { spawn, ChildProcess } from 'child_process';
import { resolveEnv } from './resolve-env'; import { resolveEnv } from './resolve-env';
@ -10,6 +10,12 @@ let deepLinkUrl: string | null = null;
const SERVER_PORT = 3000; const SERVER_PORT = 3000;
const PROTOCOL = 'surfsense'; const PROTOCOL = 'surfsense';
// TODO: Hardcoded URL is fragile — production domain may change and
// self-hosted users have their own. Two options:
// 1. Load from .env file using dotenv — users edit the file to change it.
// 2. Backend endpoint (GET /api/v1/config/frontend-url) that returns
// the backend's NEXT_FRONTEND_URL — automatic, no file to manage.
const HOSTED_FRONTEND_URL = 'https://surfsense.net';
function getStandalonePath(): string { function getStandalonePath(): string {
if (isDev) { if (isDev) {
@ -104,6 +110,14 @@ function createWindow() {
return { action: 'deny' }; return { action: 'deny' };
}); });
// Intercept backend OAuth redirects targeting the hosted web frontend
// and rewrite them to localhost so the user stays in the desktop app.
const filter = { urls: [`${HOSTED_FRONTEND_URL}/*`] };
session.defaultSession.webRequest.onBeforeRequest(filter, (details, callback) => {
const rewritten = details.url.replace(HOSTED_FRONTEND_URL, `http://localhost:${SERVER_PORT}`);
callback({ redirectURL: rewritten });
});
if (isDev) { if (isDev) {
mainWindow.webContents.openDevTools(); mainWindow.webContents.openDevTools();
} }

View file

@ -35,15 +35,6 @@ function LoginContent() {
localStorage.setItem("surfsense_redirect_path", decodeURIComponent(returnUrl)); localStorage.setItem("surfsense_redirect_path", decodeURIComponent(returnUrl));
} }
// Desktop app: persist login source so TokenHandler can redirect to
// the surfsense:// deep link after the OAuth round-trip completes.
const source = searchParams.get("source");
if (source === "desktop") {
sessionStorage.setItem("surfsense_login_source", "desktop");
} else {
sessionStorage.removeItem("surfsense_login_source");
}
// Show registration success message // Show registration success message
if (registered === "true") { if (registered === "true") {
toast.success(t("register_success"), { toast.success(t("register_success"), {

View file

@ -60,15 +60,6 @@ const TokenHandler = ({
setRefreshToken(refreshToken); setRefreshToken(refreshToken);
} }
// Desktop app: redirect tokens to the Electron deep link handler
const loginSource = sessionStorage.getItem("surfsense_login_source");
if (loginSource === "desktop") {
sessionStorage.removeItem("surfsense_login_source");
const deepLink = `surfsense://auth/callback?token=${token}${refreshToken ? `&refresh_token=${refreshToken}` : ""}`;
window.location.href = deepLink;
return;
}
// Check if there's a saved redirect path from before the auth flow // Check if there's a saved redirect path from before the auth flow
const savedRedirectPath = getAndClearRedirectPath(); const savedRedirectPath = getAndClearRedirectPath();