From 4f7c9c332bb2ba86acef7098d70b747966d317ef Mon Sep 17 00:00:00 2001
From: Linus Rath <139418639+rathlinus@users.noreply.github.com>
Date: Sat, 25 Apr 2026 02:53:12 +0200
Subject: [PATCH] feat: enhance OAuth auto-setup with dialog and validation for
origin and issuer URLs
---
app/admin/auth/page.tsx | 128 ++++++++++++++++++++++++-----
app/api/admin/oauth/setup/route.ts | 27 ++++--
2 files changed, 130 insertions(+), 25 deletions(-)
diff --git a/app/admin/auth/page.tsx b/app/admin/auth/page.tsx
index 1786aba4..426f9bab 100644
--- a/app/admin/auth/page.tsx
+++ b/app/admin/auth/page.tsx
@@ -70,16 +70,22 @@ export default function AdminAuthPage() {
}
const [setupRunning, setSetupRunning] = useState(false);
+ const [setupOpen, setSetupOpen] = useState(false);
+ const [setupOrigin, setSetupOrigin] = useState('');
+ const [setupIssuer, setSetupIssuer] = useState('');
const [setupOauthOnly, setSetupOauthOnly] = useState(false);
- async function handleAutoSetup() {
+ function openSetupDialog() {
if (typeof window === 'undefined') return;
- const oauthOnlyText = setupOauthOnly ? '\n\n • Disable password login (OAuth only)' : '';
- const ok = window.confirm(
- `Auto-configure OAuth between this webmail and the connected Stalwart server?\n\nThis will:\n • Create or update an OAuth client called "bulwark-webmail" on the Stalwart server\n • Generate a new client secret\n • Register redirect URIs for ${window.location.origin}\n • Save OAuth settings to admin config (survives env changes)${oauthOnlyText}\n\nYour Stalwart user must have admin permissions.`
- );
- if (!ok) return;
+ const origin = window.location.origin;
+ const jmapUrl = (currentValue('jmapServerUrl') as string | undefined)?.replace(/\/+$/, '') || '';
+ setSetupOrigin(origin);
+ setSetupIssuer(jmapUrl || origin);
+ setSetupOauthOnly(currentValue('oauthOnly') === true);
+ setSetupOpen(true);
+ }
+ async function handleAutoSetup() {
setSetupRunning(true);
setMessage(null);
try {
@@ -87,7 +93,8 @@ export default function AdminAuthPage() {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
- origin: window.location.origin,
+ origin: setupOrigin.trim().replace(/\/+$/, ''),
+ issuerUrl: setupIssuer.trim().replace(/\/+$/, ''),
oauthOnly: setupOauthOnly,
}),
});
@@ -95,9 +102,10 @@ export default function AdminAuthPage() {
if (res.ok) {
setMessage({
type: 'success',
- text: `OAuth client ${data.action} on Stalwart. ${data.redirectUriCount} redirect URI(s) registered. Webmail config updated.`,
+ text: `OAuth client ${data.action} on Stalwart (${data.issuerUrl}). ${data.redirectUriCount} redirect URI(s) registered for ${data.origin}.`,
});
setEdits({});
+ setSetupOpen(false);
await fetchConfig();
} else {
const detail = data.detail ? ` (${typeof data.detail === 'string' ? data.detail : JSON.stringify(data.detail).slice(0, 200)})` : '';
@@ -110,6 +118,9 @@ export default function AdminAuthPage() {
}
}
+ const setupOriginValid = /^https?:\/\/[^/]+$/.test(setupOrigin.trim().replace(/\/+$/, ''));
+ const setupIssuerValid = /^https?:\/\/[^/]+$/.test(setupIssuer.trim().replace(/\/+$/, ''));
+
const hasEdits = Object.keys(edits).length > 0;
if (loading) {
@@ -153,19 +164,9 @@ export default function AdminAuthPage() {
Registers an OAuth client on the connected Stalwart server, generates a client secret, and saves the settings here.
Requires your Stalwart account to have admin permissions.
-