# flarum-msteams-webhook Flarum-Erweiterung zur Spiegelung ausgewählter Flarum-Benachrichtigungen in den **Microsoft Teams Activity Feed**. Die Erweiterung ergänzt Flarum um einen eigenen Benachrichtigungskanal namens **Microsoft Teams**. Benutzer können diesen Kanal in ihren persönlichen Benachrichtigungseinstellungen aktivieren. Neue Aktivitäten – beispielsweise Antworten in gefolgten Diskussionen oder Erwähnungen – werden anschließend über Microsoft Graph an den Teams Activity Feed des jeweiligen Benutzers gesendet. Beim Öffnen einer Teams-Benachrichtigung erscheint eine schlanke Zwischenansicht im persönlichen Teams-Tab. Diese enthält einen Link zur zugehörigen Flarum-Diskussion beziehungsweise direkt zum konkreten Beitrag. Das eigentliche Forum muss dadurch nicht in Microsoft Teams eingebettet werden und kann weiterhin mit restriktiven Frame-Sicherheitsheadern geschützt bleiben. > **Projektstatus:** Interne Erweiterung für Flarum 1.8. Vor einem produktiven Einsatz sollten Konfiguration, Berechtigungen, Logging und Fehlerbehandlung an die jeweilige Umgebung angepasst und getestet werden. ## Funktionsumfang - eigener Flarum-Benachrichtigungskanal `teams` - rudimentäres Admin-UI zur Konfiguration der Microsoft-Graph-Anbindung - Erweiterung der persönlichen Flarum-Benachrichtigungseinstellungen um Microsoft Teams - asynchroner Versand über einen dedizierten Queue-Job - Auflösung des Microsoft-Graph-Zielbenutzers über E-Mail-Adresse, UPN oder eine Benutzerpräferenz - Abruf eines App-only Access Tokens über Microsoft Entra ID - Versand von Activity-Feed-Benachrichtigungen über Microsoft Graph - Vorschautext mit auslösendem Benutzer und Diskussionstitel - Teams Deep Link zu einem persönlichen Teams-Tab - Übergabe der konkreten Flarum-Ziel-URL über `context.subEntityId` - Auslesen des Ziels im Teams-Tab über TeamsJS v2 und `context.page.subPageId` - sichere Zwischenansicht mit validiertem Link zur Diskussion oder zum Beitrag - Test-Command für einzelne Flarum-Benutzer ## Unterstützte Benachrichtigungstypen Die Erweiterung enthält Unterstützung beziehungsweise Textvorlagen für folgende Flarum-Typen: - `newPost` – neue Antwort in einer gefolgten Diskussion - `postMentioned` – Erwähnung in einem Beitrag - `userMentioned` – Benutzererwähnung - `postLiked` – eigener Beitrag wurde mit „Gefällt mir“ markiert - `discussionRenamed` – Diskussion wurde umbenannt - `teamsTest` – interne Testbenachrichtigung des CLI-Commands Welche Typen tatsächlich in den persönlichen Einstellungen angeboten oder standardmäßig aktiviert werden, wird durch die registrierten Flarum-Blueprints und die Frontend-Erweiterung bestimmt. ## Systemvoraussetzungen - PHP 8.1 oder neuer - Flarum 1.8 - Composer - Node.js und npm zum Bauen des Flarum-Frontends - ein funktionierender Flarum-Queue-Worker - öffentlich beziehungsweise für Microsoft Teams erreichbare HTTPS-URL - Microsoft-Entra-App-Registrierung mit den erforderlichen Microsoft-Graph-Berechtigungen - installierte beziehungsweise im Mandanten veröffentlichte Microsoft-Teams-App Relevante Composer-Abhängigkeiten: ```json { "php": "^8.1", "flarum/core": "^1.8", "guzzlehttp/guzzle": "^7.8" } ``` ## Architektur und Ablauf Der Versand läuft vereinfacht über folgende Komponenten: ```text Flarum Notification Blueprint -> TeamsNotificationDriver -> SendTeamsActivityNotificationJob -> TeamsActivityNotifier -> NotificationPayloadFactory -> Microsoft Graph sendActivityNotification -> Microsoft Teams Activity Feed ``` ### Notification Driver `TeamsNotificationDriver` ist in `extend.php` als Flarum Notification Driver registriert: ```php (new Extend\Notification()) ->driver('teams', TeamsNotificationDriver::class, []), ``` Der Driver prüft die Benachrichtigungseinstellungen der Empfänger und legt den Versand als `SendTeamsActivityNotificationJob` in der Queue `teams` ab. ### Queue-Job `SendTeamsActivityNotificationJob` führt den eigentlichen Versand außerhalb des Web-Requests aus. Der Job besitzt Wiederholungs- und Backoff-Einstellungen, damit vorübergehende Graph- oder Netzwerkfehler nicht sofort zum endgültigen Verlust einer Nachricht führen. Nach Änderungen am PHP-Code müssen lang laufende Queue-Worker neu gestartet werden, da sie andernfalls weiterhin bereits geladene Klassen verwenden können. ### Token Provider `GraphTokenProvider` ruft über den OAuth-2.0-Client-Credentials-Flow ein App-only Access Token bei Microsoft Entra ID ab. Tenant-ID, Client-ID und Client Secret werden aus den Flarum-Einstellungen gelesen. Secrets dürfen weder in das Git-Repository eingecheckt noch in Logdateien geschrieben werden. ### Zielauflösung `TargetResolver` ordnet einen Flarum-Benutzer einem Microsoft-Graph-Benutzer zu. Unterstützt werden – abhängig von der Admin-Konfiguration – unter anderem: - Flarum-E-Mail-Adresse - Microsoft Entra User Principal Name (UPN) - benutzerdefinierte Flarum-Präferenz Der aufgelöste Wert wird für den Graph-Endpunkt verwendet: ```text POST https://graph.microsoft.com/v1.0/users/{target}/teamwork/sendActivityNotification ``` ### Payload-Erzeugung `NotificationPayloadFactory` erstellt den Request-Body für den Microsoft Teams Activity Feed. Der Payload enthält unter anderem: - `topic.source` - `topic.value` - `topic.webUrl` - `activityType` - `previewText` - `templateParameters` Aktuell wird `systemDefault` als Activity Type verwendet. Dadurch ist kein eigener Activity Type im Teams-Manifest erforderlich. Für Discussion- und Post-Subjects wird eine passende Ziel-URL erzeugt: ```text https://forum.example/d/{discussionId} https://forum.example/d/{discussionId}/{postNumber} ``` Die Ziel-URL wird nicht direkt im Teams-Frame geladen. Stattdessen wird sie im Deep Link als `context.subEntityId` an den persönlichen Teams-Tab übergeben. ## Zwischenansicht im Teams-Tab Die Teams-App lädt als Personal Tab eine statische Seite, beispielsweise: ```text https://forum.example/static/flarum-notify-v2.html ``` Die Seite bindet TeamsJS ein und liest den Deep-Link-Kontext aus: ```javascript await window.microsoftTeams.app.initialize(); const context = await window.microsoftTeams.app.getContext(); const target = context.page.subPageId; ``` Anschließend wird nur dann ein Link angezeigt, wenn: - die Ziel-URL dieselbe Origin wie die Landing Page besitzt und - der Pfad mit `/d/` beginnt. Dadurch kann `subEntityId` nicht als offener Redirect auf fremde Domains missbraucht werden. Die Seite unterstützt zusätzlich einen direkten Browser-Test über einen Query-Parameter: ```text https://forum.example/static/flarum-notify-v2.html?target=https%3A%2F%2Fforum.example%2Fd%2F123%2F17 ``` ## Teams-App-Manifest Das Teams-Manifest benötigt mindestens einen persönlichen statischen Tab und die Forum-Domain in `validDomains`. Beispiel: ```json { "staticTabs": [ { "entityId": "YOUR_TEAMS_ENTITY_ID", "name": "Flarum Notify", "contentUrl": "https://forum.example/static/flarum-notify-v2.html", "websiteUrl": "https://forum.example/static/flarum-notify-v2.html", "scopes": [ "personal" ], "context": [ "personalTab" ] } ], "validDomains": [ "forum.example" ] } ``` Die folgenden Werte müssen zwischen Manifest und Erweiterung übereinstimmen: - Teams App ID - Entity ID des Personal Tabs - öffentliche Forum-Basis-URL ## Microsoft Entra ID und Graph Für den App-only-Versand werden eine Microsoft-Entra-App-Registrierung und geeignete Microsoft-Graph-Anwendungsberechtigungen benötigt. Die konkrete Berechtigungskonfiguration hängt vom verwendeten Graph-Endpunkt, dem Mandanten und den internen Sicherheitsrichtlinien ab. Erforderliche Konfigurationswerte sind typischerweise: - Tenant ID - Client ID - Client Secret - Teams App ID - Forum Base URL - Strategie zur Auflösung des Zielbenutzers Nach dem Hinzufügen von Application Permissions ist in der Regel eine Administratorzustimmung im Microsoft-Entra-Mandanten erforderlich. ## Flarum-Admin-Einstellungen Das Admin-Frontend enthält Einstellungen für: - Aktivierung der Erweiterung - Tenant ID - Client ID - Client Secret - Forum Base URL - User Lookup Strategy - UPN Preference Key - User Preference Key - Activity Type - Teams App ID - optionale Icon ID - HTTP Timeout Beispiel für die Forum Base URL: ```text https://forum.example ``` Keinen abschließenden Pfad wie `/public`, `/api` oder `/static` eintragen. ## Installation ### 1. Erweiterung bereitstellen Das Repository muss als Composer-Paket beziehungsweise lokales Flarum-Paket verfügbar sein. Danach im Flarum-Root die Abhängigkeiten beziehungsweise den Autoloader aktualisieren: ```bash cd /var/www/html/flarum composer dump-autoload ``` Je nach Installationsart kann zuvor ein `composer require` oder ein Composer-Path-Repository erforderlich sein. ### 2. Admin- und Forum-Frontend bauen ```bash cd js npm install npm run build ``` Für die Entwicklung mit Watch-Modus: ```bash cd js npm install npm run dev ``` ### 3. Flarum-Cache leeren Im Flarum-Root: ```bash php flarum cache:clear ``` ### 4. Queue-Worker neu starten Bei Supervisor beispielsweise: ```bash sudo supervisorctl restart all sudo supervisorctl status ``` In einer produktiven Umgebung sollte möglichst nur die betroffene Worker-Gruppe neu gestartet werden. ## Statische Teams-Dateien Die Landing Page besteht aus: ```text flarum-notify-v2.html flarum-notify-v2.js ``` Bei einer Apache-Konfiguration mit folgendem Alias: ```apache Alias "/static" "/var/www/html/static" ``` liegen die Dateien beispielsweise hier: ```text /var/www/html/static/flarum-notify-v2.html /var/www/html/static/flarum-notify-v2.js ``` ## Apache-, CSP- und Frame-Konfiguration Teams stellt Personal Tabs in einem eingebetteten Browserkontext dar. Ein globales ```http X-Frame-Options: DENY ``` oder ```http Content-Security-Policy: frame-ancestors 'none' ``` blockiert deshalb die Landing Page und erzeugt im Teams-Desktop typischerweise eine weiße Fläche. Das eigentliche Forum kann weiterhin global gegen Framing geschützt bleiben. Nur die statische Landing Page sollte für Microsoft Teams freigegeben werden. Beispiel für Apache: ```apache Header always unset X-Frame-Options Header always unset Content-Security-Policy Header always unset Content-Security-Policy-Report-Only Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://res.cdn.office.net; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'self' https://teams.microsoft.com https://*.teams.microsoft.com https://*.cloud.microsoft; base-uri 'self'; form-action 'self'" Header always set Cache-Control "no-cache, must-revalidate, max-age=0" ``` Anschließend: ```bash sudo apache2ctl configtest sudo systemctl reload apache2 ``` Header prüfen: ```bash curl -sSI https://forum.example/static/flarum-notify-v2.html ``` Für die Landing Page darf kein restriktiver `X-Frame-Options`-Header mehr ausgeliefert werden. Die CSP muss die benötigten Teams-Hosts als `frame-ancestors` und das TeamsJS-CDN unter `script-src` erlauben. Das eigentliche Forum – insbesondere `/d/...`, Login und Admin-Bereiche – muss für diesen Ansatz nicht in einem Frame freigegeben werden. ## Cache-Verhalten in Microsoft Teams Microsoft Teams kann persönliche Tabs im Desktop-Client im Speicher halten beziehungsweise suspendieren. Änderungen an HTML oder JavaScript werden deshalb nicht immer bei jedem Klick sofort neu geladen. Empfehlungen: - Landing-Page-HTML mit `Cache-Control: no-cache, must-revalidate` ausliefern - JavaScript bei Änderungen versionieren, zum Beispiel: ```html ``` - nach größeren Änderungen Teams vollständig beenden und neu starten - ein manuelles Löschen des vollständigen Teams-Caches sollte im Normalbetrieb nicht erforderlich sein ## Test-Command Eine Testbenachrichtigung kann an einen einzelnen Flarum-Benutzer gesendet werden: ```bash php flarum teams:test-user ``` Beispiel: ```bash php flarum teams:test-user 42 ``` Nur das aufgelöste Graph-Ziel anzeigen: ```bash php flarum teams:test-user 42 --show-target ``` Ziel testweise überschreiben: ```bash php flarum teams:test-user 42 --target user@example.com ``` Der Test-Blueprint besitzt keinen realen Discussion- oder Post-Subject. Deshalb führt eine reine Testbenachrichtigung üblicherweise nicht zu einem konkreten Beitragslink, sondern zur allgemeinen Zwischenansicht. ## Benutzerkonfiguration Die Erweiterung ergänzt die persönlichen Flarum-Benachrichtigungseinstellungen um Microsoft Teams. Benutzer können Teams für die angebotenen Benachrichtigungstypen ein- oder ausschalten. Wenn ein Benutzer keine Teams-Benachrichtigungen erhält, sollten folgende Punkte geprüft werden: 1. Ist Microsoft Teams für den betreffenden Typ in Flarum aktiviert? 2. Kann `TargetResolver` eine E-Mail-Adresse oder einen UPN auflösen? 3. Ist die Teams-App für den Benutzer installiert beziehungsweise verfügbar? 4. Besitzt die Entra-App die benötigten Berechtigungen und Admin Consent? 5. Läuft der Queue-Worker? 6. Enthalten Worker- und Supervisor-Logs Graph-Fehler? ## Fehlersuche ### Weiße Fläche im Teams-Desktop Response Header prüfen: ```bash curl -sSI https://forum.example/static/flarum-notify-v2.html ``` Typische Ursachen: - `X-Frame-Options: DENY` - `X-Frame-Options: SAMEORIGIN` - `frame-ancestors 'none'` - `frame-ancestors 'self'` ohne Teams-Domains ### Seite wird ohne CSS dargestellt Wenn CSS als `