No description
  • PHP 65.2%
  • JavaScript 25%
  • HTML 9.8%
Find a file
2026-07-28 08:01:59 +00:00
js letztes npm build 2026-05-06 16:46:28 +02:00
locale aufgeräumt 2026-05-06 16:14:25 +02:00
src jetzt funktioniert der notifier! 2026-07-21 18:26:17 +02:00
.gitignore letztes npm build 2026-05-06 16:46:28 +02:00
composer.json json gefixt2 2026-04-15 12:40:29 +00:00
extend.php aufräumen#2 2026-05-06 16:39:42 +02:00
flarum-notifiy-v2.js link unterhalb "forum post" auch klickbar 2026-07-28 08:01:26 +00:00
flarum-notify-v2.html link unterhalb "forum post" auch klickbar 2026-07-28 08:01:59 +00:00
package-lock.json forum.js hatte gefehlt 2026-05-06 09:49:56 +02:00
README.md readme aktualisiert 2026-07-28 06:39:11 +00:00

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:

{
  "php": "^8.1",
  "flarum/core": "^1.8",
  "guzzlehttp/guzzle": "^7.8"
}

Architektur und Ablauf

Der Versand läuft vereinfacht über folgende Komponenten:

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:

(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:

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:

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:

https://forum.example/static/flarum-notify-v2.html

Die Seite bindet TeamsJS ein und liest den Deep-Link-Kontext aus:

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:

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:

{
  "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:

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:

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

cd js
npm install
npm run build

Für die Entwicklung mit Watch-Modus:

cd js
npm install
npm run dev

3. Flarum-Cache leeren

Im Flarum-Root:

php flarum cache:clear

4. Queue-Worker neu starten

Bei Supervisor beispielsweise:

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:

flarum-notify-v2.html
flarum-notify-v2.js

Bei einer Apache-Konfiguration mit folgendem Alias:

Alias "/static" "/var/www/html/static"

liegen die Dateien beispielsweise hier:

/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

X-Frame-Options: DENY

oder

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:

<IfModule mod_headers.c>
    <LocationMatch "^/static/flarum-notify-v2\.html$">
        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"
    </LocationMatch>
</IfModule>

Anschließend:

sudo apache2ctl configtest
sudo systemctl reload apache2

Header prüfen:

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:
<script src="/static/flarum-notify-v2.js?v=20260728-1" defer></script>
  • 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:

php flarum teams:test-user <flarumUserId>

Beispiel:

php flarum teams:test-user 42

Nur das aufgelöste Graph-Ziel anzeigen:

php flarum teams:test-user 42 --show-target

Ziel testweise überschreiben:

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:

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 <style>-Block in der HTML-Datei enthalten ist, benötigt die CSP entweder einen Hash/Nonce oder – für diese kleine interne statische Seite – beispielsweise:

style-src 'self' 'unsafe-inline'

Langfristig kann das CSS in eine separate Datei ausgelagert werden, damit 'unsafe-inline' nicht erforderlich ist.

Prüfen, ob die PHP-Datei den Deep-Link-Kontext erzeugt:

grep -nE 'subEntityId|context|subjectUrl' \
  vendor/sbp-jm/flarum-msteams-webhook/src/Support/NotificationPayloadFactory.php

Prüfen, ob die Landing Page TeamsJS lädt und page.subPageId ausliest:

grep -nE 'microsoftTeams|subPageId|getContext|initialize' \
  /var/www/html/static/flarum-notify-v2.js

Nur neu versendete Activity-Feed-Einträge enthalten den aktualisierten Deep Link. Bereits vorhandene Teams-Aktivitäten werden nicht nachträglich geändert.

Änderungen am PHP-Code werden nicht verwendet

Queue-Worker neu starten:

php flarum cache:clear
sudo supervisorctl restart all

Queue und Supervisor prüfen

sudo supervisorctl status

Logs – abhängig von der lokalen Supervisor-Konfiguration – beispielsweise:

sudo supervisorctl tail \
  flarum-teams-worker:flarum-teams-worker_00 \
  stderr

HTTP-Aufrufe der Landing Page beobachten

sudo tail -f /var/log/apache2/access.log |
  grep --line-buffered 'flarum-notify-v2'

Teams kann den Tab nach dem ersten Laden im Speicher halten. Weitere Klicks müssen daher nicht zwingend neue HTTP-Requests erzeugen.

Projektstruktur

Wichtige Dateien und Verzeichnisse:

extend.php
composer.json
js/
  admin.js
  forum.js
  src/admin/
  src/forum/
locale/
  de.yml
  en.yml
src/
  Console/
    TestTeamsUserCommand.php
  Job/
    SendTeamsActivityNotificationJob.php
  Notification/
    TeamsNotificationDriver.php
    TestActivityBlueprint.php
  Service/
    GraphTokenProvider.php
    TeamsActivityNotifier.php
  Support/
    NotificationPayloadFactory.php
    TargetResolver.php

Im Repository können zusätzlich ältere oder experimentelle Klassen wie TeamsChannel oder TeamsDriver vorhanden sein. Der aktuell in extend.php registrierte Einstiegspunkt ist TeamsNotificationDriver.

Sicherheitshinweise

  • Client Secrets niemals im Repository speichern.
  • Tokens und vollständige Graph-Payloads nur kurzfristig und geschützt protokollieren.
  • Die Landing Page darf nur Links zur eigenen Forum-Origin akzeptieren.
  • Das eigentliche Forum nicht pauschal für fremde Frames freigeben.
  • HTTPS ist verpflichtend.
  • Berechtigungen der Entra-App nach dem Least-Privilege-Prinzip vergeben.
  • Produktive Secrets regelmäßig rotieren.
  • Logdateien auf Benutzerkennungen, E-Mail-Adressen und Diskussionstitel prüfen und angemessen schützen.

Entwicklung

Frontend-Abhängigkeiten installieren:

cd js
npm install

Produktions-Build:

npm run build

Watch-Modus:

npm run dev

Formatierung:

npm run format

Nach einem Frontend-Build:

cd /var/www/html/flarum
php flarum cache:clear

Nach PHP-Änderungen zusätzlich den Queue-Worker neu starten.

Lizenz

MIT – siehe composer.json beziehungsweise eine vorhandene LICENSE-Datei.