From f3bf6efedc345b7ea3e01a7e3170fdad26b0d281 Mon Sep 17 00:00:00 2001 From: Matthieu MALVACHE Date: Thu, 16 Apr 2026 23:12:38 +0200 Subject: [PATCH] fix(oauth): fail with a clear message when SSO is loaded over plain HTTP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OAuth2/OIDC with PKCE needs crypto.subtle.digest(), which browsers only expose in secure contexts (HTTPS or localhost). Loading the webmail over http://host without a TLS proxy in front surfaced as "TypeError: can't access property 'digest', crypto.subtle is undefined" when the user clicked "Sign in with SSO" — unhelpful. Guard handleOAuthLogin with window.isSecureContext + a crypto.subtle check and surface a translated message ("SSO requires a secure connection…") in an amber warning banner instead. Key added to all 10 locales. Closes #23. --- app/[locale]/login/page.tsx | 19 +++++++++++++++++++ locales/de/common.json | 3 ++- locales/en/common.json | 3 ++- locales/es/common.json | 3 ++- locales/fr/common.json | 3 ++- locales/it/common.json | 3 ++- locales/ja/common.json | 3 ++- locales/nl/common.json | 3 ++- locales/pt/common.json | 3 ++- locales/ru/common.json | 3 ++- locales/uk/common.json | 3 ++- 11 files changed, 39 insertions(+), 10 deletions(-) diff --git a/app/[locale]/login/page.tsx b/app/[locale]/login/page.tsx index 16d8550..8130da3 100644 --- a/app/[locale]/login/page.tsx +++ b/app/[locale]/login/page.tsx @@ -40,6 +40,7 @@ export default function LoginPage() { const [oauthDiscoveryDone, setOauthDiscoveryDone] = useState(false); const [oauthLoading, setOauthLoading] = useState(false); const [oauthRetryCount, setOauthRetryCount] = useState(0); + const [oauthLocalError, setOauthLocalError] = useState(null); const suggestionsRef = useRef(null); const inputRef = useRef(null); @@ -256,6 +257,17 @@ export default function LoginPage() { const handleOAuthLogin = async () => { if (!oauthMetadata || !oauthClientId) return; + + // PKCE needs Web Crypto (SubtleCrypto.digest). That API is only exposed + // in secure contexts — i.e. HTTPS or localhost. Fail loudly before we + // try to call it so the user sees a fix-this message instead of a + // mystery "crypto.subtle is undefined" TypeError. + if (typeof window === "undefined" || !window.isSecureContext || typeof crypto?.subtle?.digest !== "function") { + setOauthLocalError(t("oauth_error.requires_https")); + return; + } + + setOauthLocalError(null); setOauthLoading(true); const verifier = generateCodeVerifier(); @@ -344,6 +356,13 @@ export default function LoginPage() { )} + {oauthLocalError && ( +
+ +

{oauthLocalError}

+
+ )} + {/* Login Form */}