From 3a82a22135efbb8b9c68e213ad8e64f36eb6d448 Mon Sep 17 00:00:00 2001 From: Fidelis Huber Date: Wed, 30 Sep 2026 23:02:09 +0200 Subject: [PATCH] Microsoft-Login auf Redirect statt Popup umgestellt (Handy/Tablet-Fix) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Popup-basierte Login/Logout und die stille Token-Erneuerung per Hintergrund-iFrame sind in installierten PWAs auf iOS/Android unzuverlässig - Popups werden blockiert oder verlieren die Verbindung zum öffnenden Fenster, und iOS' ITP blockiert oft den iFrame-Speicherzugriff für die stille Erneuerung. Das zwang Monteure am Handy/Tablet zu ständigen Neuanmeldungen, während es am PC funktionierte. Login/Logout laufen jetzt über einen vollen Redirect, scheiternde stille Token-Erneuerung fällt sauber auf einen Redirect zurück statt mit einem Fehler abzubrechen, und storeAuthStateInCookie ist aktiviert (Microsofts eigene Empfehlung für Safari/iOS). Co-Authored-By: Claude Sonnet 5 --- client/src/components/Header.tsx | 6 +++++- client/src/lib/api.ts | 13 +++++++++++-- client/src/lib/authConfig.ts | 6 +++++- client/src/main.tsx | 8 +++++++- client/src/pages/Login.tsx | 7 +++++-- 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/client/src/components/Header.tsx b/client/src/components/Header.tsx index 846e1a5..f30a0dc 100644 --- a/client/src/components/Header.tsx +++ b/client/src/components/Header.tsx @@ -18,7 +18,11 @@ export default function Header({ localUser, onLocalLogout }: { localUser: string const [themeMode, setThemeMode] = useState(getStoredTheme()); function logout() { - if (account) instance.logoutPopup(); + if (account) { + // Redirect statt Popup, aus demselben Grund wie beim Login (siehe Login.tsx). + instance.logoutRedirect(); + return; + } onLocalLogout(); } diff --git a/client/src/lib/api.ts b/client/src/lib/api.ts index 1695ea8..be04a07 100644 --- a/client/src/lib/api.ts +++ b/client/src/lib/api.ts @@ -9,8 +9,17 @@ async function authHeader(): Promise { } const account = msalInstance.getAllAccounts()[0]; if (!account) throw new Error('Nicht angemeldet.'); - const result = await msalInstance.acquireTokenSilent({ scopes: ['openid', 'profile'], account }); - return { Authorization: `Bearer ${result.idToken}`, 'Content-Type': 'application/json' }; + try { + const result = await msalInstance.acquireTokenSilent({ scopes: ['openid', 'profile'], account }); + return { Authorization: `Bearer ${result.idToken}`, 'Content-Type': 'application/json' }; + } catch { + // Silent Renewal (per Hintergrund-iFrame) scheitert auf iOS/Safari häufig, weil ITP + // Drittanbieter-Speicherzugriff im iFrame blockiert. Statt mit einem kryptischen + // Fehler abzubrechen, sauber per Redirect neu anmelden – der Nutzer landet danach + // wieder genau hier. + await msalInstance.acquireTokenRedirect({ scopes: ['openid', 'profile'], account }); + throw new Error('Anmeldung wird erneuert …'); + } } async function req(url: string, init?: RequestInit): Promise { diff --git a/client/src/lib/authConfig.ts b/client/src/lib/authConfig.ts index c3a850b..6c35091 100644 --- a/client/src/lib/authConfig.ts +++ b/client/src/lib/authConfig.ts @@ -14,7 +14,11 @@ const msalConfig: Configuration = { cache: { // localStorage statt sessionStorage: bleibt erhalten, wenn die (installierte) App // geschlossen und neu gestartet wird – sonst müsste man sich jedes Mal neu anmelden. - cacheLocation: 'localStorage' + cacheLocation: 'localStorage', + // Von Microsoft explizit für Safari/iOS empfohlen: hält den Auth-State zusätzlich + // in einem Cookie, da Safaris ITP die interne Silent-Renewal-/Redirect-Logik sonst + // gerne stört – genau das Symptom "am Handy ständig neu anmelden müssen". + storeAuthStateInCookie: true } }; diff --git a/client/src/main.tsx b/client/src/main.tsx index eef2fe9..c522706 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -8,7 +8,13 @@ import './styles.css'; initTheme(); -msalInstance.initialize().then(() => { +msalInstance.initialize().then(() => + // Verarbeitet die Rückkehr von loginRedirect() (Popups sind in installierten PWAs auf + // Handys/Tablets unzuverlässig – deshalb nutzt der Microsoft-Login einen vollen + // Redirect statt eines Popups, siehe Login.tsx). + msalInstance.handleRedirectPromise() +).then(result => { + if (result?.account) msalInstance.setActiveAccount(result.account); ReactDOM.createRoot(document.getElementById('root')!).render( diff --git a/client/src/pages/Login.tsx b/client/src/pages/Login.tsx index d9ed920..03e1e21 100644 --- a/client/src/pages/Login.tsx +++ b/client/src/pages/Login.tsx @@ -14,11 +14,14 @@ export default function Login({ onLocalLogin }: { onLocalLogin: (name: string) = setBusy(true); setErr(''); try { - await instance.loginPopup({ scopes: ['openid', 'profile'] }); + // Redirect statt Popup: Popups werden in installierten PWAs auf Handys/Tablets + // (Homescreen-App) von iOS/Android oft blockiert oder verlieren die Verbindung + // zum öffnenden Fenster – dadurch musste man sich dort ständig neu anmelden. + await instance.loginRedirect({ scopes: ['openid', 'profile'] }); } catch (e) { setErr(e instanceof Error ? e.message : String(e)); + setBusy(false); } - setBusy(false); } async function submitLocal(e: FormEvent) {