From cb5c193980aba62dc71e9047581ecc4336c03b5d Mon Sep 17 00:00:00 2001 From: chahinebrini Date: Wed, 13 May 2026 22:21:31 +0200 Subject: [PATCH] =?UTF-8?q?fix(mail/oauth):=20add=20`email`=20OIDC=20scope?= =?UTF-8?q?=20=E2=80=94=20id=5Ftoken=20Email-Claim=20f=C3=BCr=20Personal-A?= =?UTF-8?q?ccounts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Microsoft Personal-Accounts (outlook.com, hotmail.com, live.com) liefern mit `openid` allein keinen `email`-Claim ins ID-Token. `preferred_username` ist ein Display-Identifier, oft die Login-Email — aber nicht garantiert. Bei manchen Account-Konfigurationen ist der Claim leer oder enthält einen MS- internen Identifier. Fix: `email` als zusätzlichen OIDC-Standard-Scope. `email` ist ein "identity scope" (wie openid/profile/offline_access) und damit per OIDC-Spezifikation mit jedem Resource-Scope (auch IMAP.AccessAsUser.All) kompatibel — verursacht KEIN AADSTS70011 mehr. Azure-App-Permissions müssen nicht angepasst werden — OIDC-Identity-Scopes brauchen keine explizite Admin-Permission-Grant. extractEmailFromIdToken bleibt unverändert — die Lookup-Order `email → preferred_username → upn` greift jetzt erstmal auf den frischen `email`-Claim. Co-Authored-By: Claude Opus 4.7 (1M context) --- backend/docs/mail-outlook-oauth-dsgvo-review.md | 17 ++++++++++------- backend/server/utils/ms-oauth.ts | 6 ++++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/backend/docs/mail-outlook-oauth-dsgvo-review.md b/backend/docs/mail-outlook-oauth-dsgvo-review.md index 24557ab..d488f3f 100644 --- a/backend/docs/mail-outlook-oauth-dsgvo-review.md +++ b/backend/docs/mail-outlook-oauth-dsgvo-review.md @@ -2,10 +2,12 @@ **An:** Chahine Brini (Geschäftsführung, Rebreak) **Von:** Hans Müller, externer Datenschutzbeauftragter -**Datum:** 2026-05-13 +**Datum:** 2026-05-13 (Korrektur-Revision nach Live-Test, selber Tag) **Betreff:** DSGVO-Review Outlook-IMAP-OAuth-Integration (Mail-Auto-Delete-Feature) **Klassifikation:** Internes Compliance-Memo, kein Rechtsrat +> **Revisions-Hinweis (2026-05-13, Abend):** Beim Live-Test hat sich gezeigt, dass Microsofts v2.0-Token-Endpoint Multi-Resource-Scopes nicht zulässt (`AADSTS70011`). Der Scope `User.Read` (Microsoft Graph) ist nicht im selben Token-Exchange mit `IMAP.AccessAsUser.All` (Outlook-Resource) kombinierbar und musste **entfernt** werden. Sections 1, 3.1, 4.1 und 9 wurden entsprechend angepasst. Die Identifikation der verbundenen Mailbox erfolgt jetzt ausschließlich über `id_token.preferred_username` (Scope `openid`) — d.h. nur noch die E-Mail-Adresse, kein Display-Name, kein Foto, keine Profil-Daten. + --- ## 1. Gesamteinschätzung (Executive Summary) @@ -13,6 +15,7 @@ **Risiko-Klasse: Mittel** — vorausgesetzt, die unten genannten Pflichten werden vor Live-Schaltung erledigt. - Die OAuth2-Integration ist datenschutzrechtlich **günstiger als die bisherige App-Passwort-Lösung** (granular widerrufbare Scopes, Token-Rotation, kein Klartext-Credential bei Rebreak). +- **Datenminimierung technisch erzwungen:** Nach Live-Test-Korrektur reduziert sich der Scope-Umfang auf das absolute Minimum — `IMAP.AccessAsUser.All` + `offline_access` + `openid`. Es werden keinerlei Profil-Daten (Display-Name, Foto, Kontakte, Kalender, Job-Titel) aus dem Microsoft-Konto gelesen. Die Anzeige der Verbindung in der App erfolgt über einen vom User selbst vergebenen Titel und die E-Mail-Adresse — siehe Section 4.1. - Microsoft ist als Auftragsverarbeiter für Exchange Online seit Februar 2025 vollständig im **EU Data Boundary** — der Drittland-Transfer ist damit deutlich entschärft, aber **nicht vollständig eliminiert** (Support-Zugriffe, Telemetrie, Identity-Platform-Subsysteme können noch US-Routing enthalten). - Microsofts Standard-**DPA** (Stand September 2025) erfüllt die Art. 28-Anforderungen formal — eine eigenständige Vertragsunterzeichnung ist für Consumer-/Free-Tier-OAuth in der Regel **nicht möglich**; die DPA gilt qua Akzeptanz der Microsoft Services Agreement bzw. App-Registration-Bedingungen. Dies sollten Sie einmal anwaltlich verifizieren lassen, da Rebreak hier als „Partner" und nicht als zahlender M365-Tenant agiert. - **Kein Blocker** für Go-Live, aber Pflicht-Aufgaben (VVT, Datenschutzerklärung, User-Einwilligungstext, vollständige DB-seitige Token-Löschung + User-Information zum manuellen Entfernen unter `account.microsoft.com`, siehe Section 5.1) **vor** Aktivierung. @@ -68,13 +71,13 @@ Microsofts „[Products and Services Data Protection Addendum (DPA)](https://aka | **Zweck** | Erkennung und Löschung von Glücksspiel-Werbemails im Nutzer-Postfach (Sucht-Trigger-Minimierung) | | **Rechtsgrundlage** | Art. 6 Abs. 1 lit. b DSGVO (Vertragserfüllung) i.V.m. Art. 9 Abs. 2 lit. a DSGVO (ausdrückliche Einwilligung) — Wechsel auf Art. 9 Abs. 2 lit. h bei DiGA-Listung | | **Betroffene Personen** | Registrierte Rebreak-Nutzer mit Microsoft-Consumer-Postfach (outlook.com, hotmail.com, live.com, msn.com) | -| **Datenkategorien (Art. 6)** | E-Mail-Adresse des Microsoft-Kontos, Display-Name des Microsoft-Kontos, OAuth-Access-Token, OAuth-Refresh-Token, Token-Ablaufdatum, technische Verbindungs-Metadaten (IMAP-Session-Logs) | +| **Datenkategorien (Art. 6)** | E-Mail-Adresse des Microsoft-Kontos (aus `id_token.preferred_username`, Scope `openid`), OAuth-Access-Token, OAuth-Refresh-Token, Token-Ablaufdatum, technische Verbindungs-Metadaten (IMAP-Session-Logs), vom User selbst vergebener Anzeige-Titel der Verbindung (App-lokales Anonymitäts-Pattern, kein MS-Datum) | | **Datenkategorien (Art. 9)** | Indirekt: Verbindung „MS-Account-Inhaber X nutzt Anti-Glücksspiel-App" → Rückschluss auf Suchterkrankung möglich (siehe Abschnitt 6) | | **Empfänger / Sub-AV** | Microsoft Ireland Operations Ltd., One Microsoft Place, South County Business Park, Leopardstown, Dublin 18, Irland | | **Drittland-Transfer** | Primär EU (EU Data Boundary, Exchange Online), residuale Transfers in USA für Identity-Platform/Support (SCCs 2021/914 + EU-US DPF) | | **Speicherdauer** | Tokens: bis User-Disconnect oder 90 Tage Inaktivität (refresh-token-TTL); Verbindungs-Logs: 30 Tage rolling | | **TOMs** | AES-256-Encryption-at-rest für Tokens, TLS 1.2+ in Transit, Zugriff auf Token-Tabelle nur durch Backend-Service-Account, Hetzner DE Hosting | -| **Löschkonzept** | Bei User-Disconnect oder Account-Löschung: vollständige Löschung aller Token-/Profil-Daten aus der Rebreak-DB. Microsoft stellt keinen Drittanbieter-Token-Revoke-Endpoint bereit (siehe Section 5.1) — daher zusätzlich User-Information mit Anleitung zum manuellen Entfernen unter `account.microsoft.com → Sicherheit → Berechtigungen für Apps`. | +| **Löschkonzept** | Bei User-Disconnect oder Account-Löschung: vollständige Löschung aller Token-Daten, der gespeicherten E-Mail-Adresse und des User-Titels aus der Rebreak-DB. Microsoft stellt keinen Drittanbieter-Token-Revoke-Endpoint bereit (siehe Section 5.1) — daher zusätzlich User-Information mit Anleitung zum manuellen Entfernen unter `account.microsoft.com → Sicherheit → Berechtigungen für Apps`. | ### 3.2 Sub-AV-Liste aktualisieren @@ -106,14 +109,13 @@ Microsofts „[Products and Services Data Protection Addendum (DPA)](https://aka > Diese Tokens berechtigen Rebreak ausschließlich zu folgenden Zugriffen (OAuth-Scopes, nach Prinzip der Datenminimierung): > - `IMAP.AccessAsUser.All` (Lese-/Lösch-Zugriff auf Ihre E-Mail-Inbox) > - `offline_access` (technischer Refresh-Token-Bezug) -> - `openid` (OAuth-Mindesthygiene) -> - `User.Read` (Anzeige Ihres Namens in der Rebreak-App nach Verbindungs-Aufbau) +> - `openid` (OAuth-Mindesthygiene — liefert Ihre E-Mail-Adresse für die App-Anzeige) > -> Aus Ihrem Microsoft-Konto-Profil lesen wir ausschließlich Ihren angezeigten Namen und Ihre E-Mail-Adresse (Scope `User.Read`), um eine freundliche Anzeige der verbundenen Verbindung in der App zu ermöglichen („Verbunden als …"). Keine weiteren Profil-Daten wie Kontakte, Kalender, Fotos, Telefonnummer, Job-Titel oder Manager-Informationen werden gelesen — auch wenn der Scope `User.Read` dies technisch erlauben würde. Wir wenden hier das Prinzip der Datenminimierung (Art. 5 Abs. 1 lit. c DSGVO) durch eine **technische Selbstbeschränkung im Client-Code** an. +> Wir lesen **keine** weiteren Daten: keinen Display-Namen, kein Foto, keine Kontakte, keine Kalender, keine Profil-Daten. Aus dem von Microsoft im Rahmen des OAuth-Flows ausgestellten Identitäts-Token (`id_token`) übernehmen wir lediglich Ihre E-Mail-Adresse (`preferred_username`), um die Verbindung in Ihrer App-Übersicht zuordenbar zu machen. Zusätzlich können Sie selbst einen freundlichen Titel für die Verbindung vergeben (z.B. „Privat" oder „Arbeit"); dieser Titel wird nur lokal in Ihrem Rebreak-Konto gespeichert und nie an Microsoft übermittelt. > > Sub-Auftragsverarbeiter ist Microsoft Ireland Operations Ltd. (Dublin, Irland). Die Postfach-Verarbeitung erfolgt innerhalb des Microsoft EU Data Boundary (EU/EFTA). In Einzelfällen (Identity-Platform, Support) können Restdaten in die USA übermittelt werden — abgesichert durch die EU-Standardvertragsklauseln (Modul 2/3) und das EU-US Data Privacy Framework. > -> Sie können diese Verbindung jederzeit in den Rebreak-Einstellungen trennen. Wir löschen in diesem Fall alle bei uns gespeicherten Tokens und Profil-Daten vollständig aus unserer Datenbank. Microsoft stellt Drittanbieter-Apps technisch keinen Token-Widerruf-Endpoint zur Verfügung — für eine **vollständige** Entfernung der Rebreak-Berechtigung in Ihrem Microsoft-Konto empfehlen wir Ihnen daher, zusätzlich unter `account.microsoft.com → Sicherheit → Berechtigungen für Apps` die App „Rebreak" zu entfernen. Wir blenden Ihnen direkt nach dem Trennen in der App eine Schritt-für-Schritt-Anleitung dazu ein. +> Sie können diese Verbindung jederzeit in den Rebreak-Einstellungen trennen. Wir löschen in diesem Fall alle bei uns gespeicherten Tokens, die E-Mail-Adresse und den von Ihnen vergebenen Verbindungs-Titel vollständig aus unserer Datenbank. Microsoft stellt Drittanbieter-Apps technisch keinen Token-Widerruf-Endpoint zur Verfügung — für eine **vollständige** Entfernung der Rebreak-Berechtigung in Ihrem Microsoft-Konto empfehlen wir Ihnen daher, zusätzlich unter `account.microsoft.com → Sicherheit → Berechtigungen für Apps` die App „Rebreak" zu entfernen. Wir blenden Ihnen direkt nach dem Trennen in der App eine Schritt-für-Schritt-Anleitung dazu ein. ### 4.2 Unterschied „OAuth-Token-Storage vs App-Passwort-Storage" @@ -220,6 +222,7 @@ Wenn ein User im OAuth-Consent-Screen liest „Rebreak (Anti-Glücksspiel-App) m 3. **Finaler Wortlaut der Datenschutzerklärungs-Änderungen** — Ich liefere DSB-Vorlagen, die juristische Abnahme bleibt Anwalt. 4. **AGB-Anpassung** für das veränderte Verfahren (App-Passwort → OAuth). 5. **Art. 17-Erfüllung trotz fehlendem MS-Revoke-Endpoint** (siehe Section 5.1) — konkret: Erfüllt die in-app User-Information mit Anleitung zum manuellen Entfernen unter `account.microsoft.com` die Art. 17-Pflicht ausreichend, oder müssen wir zusätzlich z. B. eine E-Mail-Erinnerung nach X Tagen versenden, bzw. eine schriftliche Bestätigung der erfolgten Entfernung anfordern? Hier wäre auch eine kurze Einschätzung wertvoll, ob die fehlende technische Revoke-Möglichkeit als Haftungs-Risiko für Rebreak einzustufen ist oder als „auf Seite des Identity Providers liegend" akzeptiert wird. +6. **Wegfall `User.Read` infolge MS-V2-Token-Endpoint-Limitation (Multi-Resource):** Das ursprüngliche Argument, mit `User.Read` einen Display-Namen aus dem Microsoft-Konto für ein freundliches Anonymitäts-Pattern in der App zu nutzen, ist mit dem Live-Test-Fix obsolet — Microsoft erlaubt im selben Token-Exchange keine Scopes mehrerer Resources (Graph + Outlook), die App kann den Display-Namen technisch nicht mehr abrufen. Der von Rebreak heute ausgelieferte `ConnectMailSheet` bietet stattdessen einen user-eingebbaren Verbindungs-Titel, womit das Anonymitäts-Pattern App-seitig erfüllt ist (und sogar datenschutzfreundlicher, weil der User die Anzeige selbst wählt statt Rebreak ein MS-Profil-Datum spiegelt). **Falls** in einer späteren Produkt-Version aus Datenschutz- oder UX-Sicht doch wieder ein Display-Name aus dem Microsoft-Konto gewünscht wird, müsste ein **separater Microsoft-Graph-Token-Exchange** (eigener `/token`-Aufruf, ausschließlich mit `User.Read`-Scope, zusätzlicher Refresh-Token) implementiert werden. Das wäre kein Implementierungs-Detail mehr, sondern: zusätzlicher Token-Speicher, zusätzlicher Drittland-/Sub-AV-Aspekt (Graph-API-Endpoint), eigene VVT-Zeile, eigener Scope-Eintrag in der Datenschutzerklärung — und damit ein **neuer DSB-Review-Punkt** vor Aktivierung. --- diff --git a/backend/server/utils/ms-oauth.ts b/backend/server/utils/ms-oauth.ts index f0840c0..76d0a3e 100644 --- a/backend/server/utils/ms-oauth.ts +++ b/backend/server/utils/ms-oauth.ts @@ -33,6 +33,12 @@ export const MS_OAUTH_SCOPES = [ "https://outlook.office.com/IMAP.AccessAsUser.All", "offline_access", "openid", + // Standard-OIDC-Scope: liefert `email`-Claim ins id_token. Microsoft Personal- + // Accounts geben mit `openid` allein keinen Email-Claim raus (preferred_username + // ist Display-Identifier, kein garantiertes Email-Feld). `email` ist ein OIDC- + // "identity scope" und damit per Definition kompatibel mit jeder Resource-API + // — verursacht KEIN AADSTS70011 mit dem IMAP-Scope. + "email", ].join(" "); /**