Some checks are pending
CI / PHP lint (7.4) (pull_request) Waiting to run
CI / PHP lint (8.0) (pull_request) Waiting to run
CI / PHP lint (8.1) (pull_request) Waiting to run
CI / PHP lint (8.2) (pull_request) Waiting to run
CI / PHP lint (8.3) (pull_request) Waiting to run
CI / PHP lint (8.4) (pull_request) Waiting to run
CI / WordPress Coding Standards (pull_request) Waiting to run
CI / WordPress.org Plugin Check (pull_request) Waiting to run
- No border or shadow around the login box on wp-login.php (form and Microsoft block, also in button-only mode); the white area stays. - Button-only mode: the "Lost your password?" link is removed through lost_password_html_link instead of CSS only, the lostpassword, retrievepassword, rp and resetpass screens redirect to the login page and allow_password_reset refuses resets – all unless the fallback link is active. - The login stylesheet is also loaded when only the form is hidden (e.g. broken connection); before, the link and form showed there. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
626 lines
37 KiB
Markdown
626 lines
37 KiB
Markdown
<div align="center">
|
||
|
||
<img src=".wordpress-org/icon.svg" width="96" height="96" alt="">
|
||
|
||
# M365 Login
|
||
|
||
**Anmeldung an WordPress mit dem Microsoft 365 / Entra ID-Konto – sicher, schlank, gestaltbar.**
|
||
|
||
[](https://wordpress.org/)
|
||
[](https://www.php.net/)
|
||
[](LICENSE)
|
||
[](docs/wordpress-org-einreichung.md)
|
||
|
||
<img src=".github/assets/button-preview.svg" width="720" alt="Login-Seite mit Microsoft-Button, Farb-Presets und Sicherheitsmerkmalen">
|
||
|
||
</div>
|
||
|
||
---
|
||
|
||
## Inhalt
|
||
|
||
- [Auf einen Blick](#auf-einen-blick)
|
||
- [Screenshots](#screenshots)
|
||
- [So funktioniert es](#so-funktioniert-es)
|
||
- [Installation](#installation)
|
||
- [Einrichtung in Microsoft Entra ID](#einrichtung-in-microsoft-entra-id)
|
||
- [Einstellungen im Backend](#einstellungen-im-backend)
|
||
- [Verbindung](#verbindung)
|
||
- [Button](#button)
|
||
- [Sicherheit](#sicherheit)
|
||
- [Gruppen-Beschränkung](#gruppen-beschränkung)
|
||
- [Nur-Button-Modus & Fallback](#nur-button-modus--fallback)
|
||
- [Eigene Login-Seite](#eigene-login-seite)
|
||
- [Benutzer-Sync](#benutzer-sync)
|
||
- [Administrator-Konten verknüpfen](#administrator-konten-verknüpfen)
|
||
- [Sicherheitskonzept](#sicherheitskonzept)
|
||
- [Shortcode & Hooks](#shortcode--hooks)
|
||
- [Fehlerbehebung](#fehlerbehebung)
|
||
- [Entwicklung](#entwicklung)
|
||
- [Einreichung bei WordPress.org](#einreichung-bei-wordpressorg)
|
||
- [FAQ](#faq)
|
||
- [Lizenz](#lizenz)
|
||
|
||
---
|
||
|
||
## Auf einen Blick
|
||
|
||
| | |
|
||
| --- | --- |
|
||
| 🔑 **Login per Microsoft** | Ein Klick auf der Anmeldeseite, Anmeldung bei Microsoft, zurück in WordPress – fertig. |
|
||
| 📧 **Zuordnung über die E-Mail-Adresse** | Der Login legt **keine Benutzer an**. Nur wer schon ein WordPress-Konto mit derselben E-Mail hat, kommt rein. |
|
||
| 🔄 **Benutzer-Sync (optional)** | Importiert Microsoft-365-Benutzer als WordPress-Konten – mit Standardrolle, zusätzlichen Rollen per Gruppen-Zuordnung, wählbaren Profilfeldern und Profilbild. In Microsoft 365 deaktivierte oder gelöschte Konten werden in WordPress deaktiviert oder gelöscht. |
|
||
| 🎨 **Gestaltbarer Button** | Text, Icon (Microsoft-Logo oder eigenes Bild), Farben, Hover-Farbe, Rahmen, Eckenradius, Position – mit Live-Vorschau und Presets. |
|
||
| 👥 **Entra-Gruppen** | Optional nur Mitglieder ausgewählter Gruppen zulassen und/oder Mitglieder bestimmter Gruppen ausschließen. Gruppen werden direkt im Backend gesucht und ausgewählt. |
|
||
| 🚪 **Nur-Button-Modus** | Passwortfelder ausblenden und Passwort-Logins sperren – mit geheimem Fallback-Link als Notausgang. |
|
||
| 🔏 **Secret oder Zertifikat** | Wahlweise Client Secret oder zertifikatsbasierte Authentifizierung (RFC 7523). Zertifikat mit einem Klick im Backend erzeugen, nur der öffentliche Teil geht zu Microsoft. |
|
||
| 🛡️ **Sicher by default** | OpenID Connect + PKCE, Signaturprüfung, Tenant-Pinning, Konto-Bindung, verschlüsseltes Secret, [Security-Audit](docs/security-audit.md). |
|
||
| 🌍 **Übersetzbar** | Englische Basis, deutsche Übersetzung (du & Sie) enthalten. |
|
||
| 📦 **WordPress.org-ready** | readme.txt, Lizenz, Uninstall, Plugin Check in CI, Build-Script. |
|
||
|
||
---
|
||
|
||
## Screenshots
|
||
|
||
> Vorschauen, gerendert aus dem echten Plugin-Markup und -CSS in einer nachgebauten WordPress-Oberfläche.
|
||
|
||
| Login-Seite | Nur-Button-Modus |
|
||
| --- | --- |
|
||
|  |  |
|
||
|
||
| M365 Login – Verbindung | M365 Login – Button |
|
||
| --- | --- |
|
||
|  |  |
|
||
|
||
<details>
|
||
<summary><strong>M365 Login – Sicherheit</strong> (Gruppen-Auswahl, Nur-Button-Modus, Fallback-Link)</summary>
|
||
|
||

|
||
|
||
</details>
|
||
|
||
---
|
||
|
||
## So funktioniert es
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
autonumber
|
||
participant B as Browser
|
||
participant WP as WordPress<br/>(M365 Login)
|
||
participant MS as Microsoft Entra ID
|
||
participant G as Microsoft Graph<br/>(optional)
|
||
|
||
B->>WP: Klick auf „Login mit Microsoft“
|
||
WP->>WP: state, nonce, PKCE-Verifier erzeugen<br/>State-Cookie setzen (HttpOnly)
|
||
WP-->>B: Redirect zu Microsoft (code_challenge, state, nonce)
|
||
B->>MS: Anmeldung beim Microsoft-Konto
|
||
MS-->>B: Redirect zurück mit code + state
|
||
B->>WP: /m365-login/callback?code=…&state=…
|
||
WP->>WP: State einmalig einlösen, Cookie prüfen
|
||
WP->>MS: Code + code_verifier + Client Secret (Server-zu-Server)
|
||
MS-->>WP: ID-Token
|
||
WP->>MS: Signaturschlüssel (JWKS, gecacht)
|
||
WP->>WP: Signatur, Issuer, Audience, Tenant, exp, Nonce prüfen
|
||
opt Gruppen-Beschränkung aktiv
|
||
WP->>G: checkMemberGroups(oid, erlaubte Gruppen)
|
||
G-->>WP: Treffer / kein Treffer
|
||
end
|
||
WP->>WP: Benutzer per E-Mail suchen, Objekt-ID abgleichen
|
||
WP-->>B: WordPress-Session, Redirect ins Dashboard
|
||
```
|
||
|
||
Tokens laufen ausschließlich zwischen deinem Server und Microsoft. Der Browser sieht nur einen Autorisierungscode, der ohne den serverseitigen PKCE-Verifier und das Client Secret wertlos ist.
|
||
|
||
---
|
||
|
||
## Installation
|
||
|
||
**Variante A – manuell (empfohlen, solange das Plugin nicht im Verzeichnis ist)**
|
||
|
||
```bash
|
||
git clone https://github.com/friloo/wp-m365-login.git
|
||
cd wp-m365-login
|
||
bash bin/build-zip.sh # erzeugt build/m365-login.zip
|
||
```
|
||
|
||
Dann in WordPress unter *Plugins → Installieren → Plugin hochladen* das ZIP hochladen und aktivieren.
|
||
Alternativ den Repo-Inhalt als Ordner `m365-login` nach `wp-content/plugins/` kopieren.
|
||
|
||
**Variante B – WordPress.org** (nach der Freigabe): *Plugins → Installieren → „M365 Login“*.
|
||
|
||
> **Voraussetzungen:** WordPress ≥ 6.0, PHP ≥ 7.4 mit OpenSSL-Erweiterung, HTTPS auf der Website (Microsoft akzeptiert `http://` nur für `localhost`).
|
||
|
||
---
|
||
|
||
## Einrichtung in Microsoft Entra ID
|
||
|
||
<details open>
|
||
<summary><strong>Schritt für Schritt (ca. 5 Minuten)</strong></summary>
|
||
|
||
1. **Redirect-URI kopieren.** In WordPress den Menüpunkt *M365 Login* öffnen; die URI steht in der Seitenleiste
|
||
(`https://deine-seite.tld/m365-login/callback`, bei einfachen Permalinks `https://deine-seite.tld/?m365-login=callback`).
|
||
2. **App registrieren.** [Microsoft Entra Admin Center](https://entra.microsoft.com/) → *App-Registrierungen → Neue Registrierung*
|
||
- Name: z. B. „WordPress Login“
|
||
- Unterstützte Kontotypen: **Nur Konten in diesem Organisationsverzeichnis** (Single Tenant)
|
||
- Umleitungs-URI: Plattform **Web**, URI aus Schritt 1
|
||
3. **IDs übernehmen.** Auf der Übersichtsseite **Anwendungs-ID (Client)** und **Verzeichnis-ID (Mandant)** kopieren → in WordPress eintragen.
|
||
4. **Authentifizierung wählen.**
|
||
- *Zertifikat (empfohlen):* In WordPress *Zertifikat erzeugen* → *.cer herunterladen* → in Entra ID *Zertifikate & Geheimnisse → Zertifikate → Zertifikat hochladen*. Thumbprint vergleichen.
|
||
- *Client Secret:* *Zertifikate & Geheimnisse → Neuer geheimer Clientschlüssel* → den **Wert** (nicht die Geheimnis-ID) in WordPress eintragen. Ablaufdatum notieren.
|
||
5. **E-Mail-Claim aktivieren** (empfohlen). *Tokenkonfiguration → Optionalen Anspruch hinzufügen → ID → `email`*.
|
||
6. **Speichern** und mit *Tenant testen* prüfen, ob Microsoft erreichbar ist.
|
||
|
||
</details>
|
||
|
||
<details>
|
||
<summary><strong>Zusätzlich für die Gruppen-Beschränkung</strong></summary>
|
||
|
||
Damit das Backend Gruppen suchen und beim Login die Mitgliedschaft prüfen kann, braucht die App-Registrierung
|
||
**Anwendungsberechtigungen** (nicht delegiert) für Microsoft Graph, jeweils mit **Administratorzustimmung**:
|
||
|
||
| Berechtigung | Wofür |
|
||
| --- | --- |
|
||
| `GroupMember.Read.All` | Gruppen im Backend suchen |
|
||
| `User.Read.All` | Mitgliedschaft beim Login prüfen (`checkMemberGroups`, inkl. verschachtelter Gruppen) |
|
||
|
||
`Directory.Read.All` deckt beides ab, ist aber weiter gefasst.
|
||
|
||
**Ohne Graph-Berechtigungen** geht es auch: Unter *Tokenkonfiguration → Gruppenanspruch hinzufügen* den `groups`-Claim
|
||
für ID-Tokens aktivieren (am besten *Der Anwendung zugewiesene Gruppen* oder *Sicherheitsgruppen*). Dann prüft das Plugin die
|
||
Mitgliedschaft direkt im Token. Gruppen-IDs lassen sich im Backend auch von Hand einfügen. Bei mehr als 200 Gruppen pro
|
||
Benutzer liefert Microsoft keinen `groups`-Claim mehr („Overage“); dann greift das Plugin automatisch auf Graph zurück.
|
||
|
||
</details>
|
||
|
||
<details>
|
||
<summary><strong>Zusätzlich für den Benutzer-Sync</strong></summary>
|
||
|
||
| Berechtigung (Anwendung, mit Administratorzustimmung) | Wofür |
|
||
| --- | --- |
|
||
| `User.Read.All` | Benutzer, Kontostatus, Profilfelder und Profilbilder lesen |
|
||
| `GroupMember.Read.All` | Nur nötig, wenn Sync-Gruppen oder Rollen-Zuordnungen verwendet werden |
|
||
|
||
Außerdem muss im Tab *Verbindung* die **Tenant-GUID** eingetragen sein (nicht `organizations`/`common`).
|
||
|
||
</details>
|
||
|
||
Wichtig: Jeder Benutzer, der sich per Microsoft anmelden soll, braucht in WordPress **dieselbe E-Mail-Adresse** wie in Microsoft 365 – von Hand angelegt oder vom [Benutzer-Sync](#benutzer-sync) importiert.
|
||
|
||
---
|
||
|
||
## Einstellungen im Backend
|
||
|
||
Menüpunkt *M365 Login* – drei Tabs (auch als Untermenüs erreichbar), ein Formular, ein Speichern-Button.
|
||
|
||
### Verbindung
|
||
|
||
| Feld | Beschreibung |
|
||
| --- | --- |
|
||
| Verzeichnis-ID (Tenant) | GUID des Tenants (empfohlen, aktiviert Tenant-Pinning) oder `organizations` / `common` / `consumers`. Im Multi-Tenant-Modus wird nur der UPN zur Zuordnung verwendet (siehe Audit H-1). |
|
||
| Anwendungs-ID (Client) | GUID der App-Registrierung. |
|
||
| Authentifizierung | **Client Secret** oder **Zertifikat** (empfohlen). Für beide Wege gibt es im Backend eine Schritt-für-Schritt-Anleitung. |
|
||
| Client Secret | Wird verschlüsselt gespeichert und nie wieder angezeigt. Leer lassen = behalten. |
|
||
| Zertifikat | *Zertifikat erzeugen* legt ein 3072-Bit-RSA-Schlüsselpaar mit selbstsigniertem Zertifikat (2 Jahre) an. Der private Schlüssel bleibt verschlüsselt auf dem Server; die `.cer`-Datei wird in Entra ID unter *Zertifikate & Geheimnisse → Zertifikate* hochgeladen. Alternativ eigenes PEM-Paar einfügen. |
|
||
| Kontoauswahl | `select_account` (Standard), `none` (bestehende Microsoft-Sitzung nutzen) oder `login` (immer Anmeldedaten verlangen). |
|
||
| Tenant testen | Lädt die OpenID-Konfiguration des Tenants – prüft ID und ausgehende Verbindung. |
|
||
|
||
### Button
|
||
|
||
| Option | Beschreibung |
|
||
| --- | --- |
|
||
| Button-Text | Standard „Sign in with Microsoft“ / „Login mit Microsoft“ (max. 80 Zeichen). |
|
||
| Trennlinien-Text | Standard „or“ / „oder“; leer = keine Trennlinie. |
|
||
| Icon | Microsoft-Logo (eingebettet) oder eigenes Bild aus der Mediathek (PNG, SVG, JPG, WebP). Ein-/ausblendbar. |
|
||
| Farben | Hintergrund, Hintergrund (Hover), Text, Rahmen – mit Farbwähler. |
|
||
| Eckenradius | 0–50 px. |
|
||
| Position | Unter dem Login-Formular (Standard) oder darüber. |
|
||
| Presets | Microsoft dunkel, Microsoft hell, Azure-Blau, WordPress-Blau. |
|
||
|
||
Alles wird live in der Vorschau angezeigt, bevor du speicherst.
|
||
|
||
### Sicherheit
|
||
|
||
| Option | Standard | Beschreibung |
|
||
| --- | --- | --- |
|
||
| Konto an Microsoft-Objekt-ID binden | an | Beim ersten Login wird die `oid` gespeichert; danach muss sie übereinstimmen. Schützt vor Übernahme, wenn eine E-Mail-Adresse in Microsoft neu vergeben wird. |
|
||
| UPN-Fallback | an | Neben dem `email`-Claim wird auch der User Principal Name gesucht – wichtig, wenn WordPress-Konten den UPN statt der Mailadresse tragen. Der Abgleich läuft in der Reihenfolge: gebundene Objekt-ID → zugewiesener UPN → Mailadresse → UPN. |
|
||
| Angemeldet bleiben | aus | 14-Tage-Session statt Browser-Session. |
|
||
| Erlaubte E-Mail-Domains | leer | Kommagetrennte Liste, z. B. `contoso.com, contoso.de`. |
|
||
|
||
### Gruppen-Beschränkung
|
||
|
||
Im Tab *Sicherheit* → **Erlaubte Entra-Gruppen**:
|
||
|
||
1. Gruppenname eintippen (oder Objekt-ID einfügen) → *Suchen*.
|
||
2. Treffer mit *Hinzufügen* übernehmen – sie erscheinen als Chips mit Name und ID.
|
||
3. Speichern. Ab jetzt darf sich nur anmelden, wer in **mindestens einer** dieser Gruppen ist (verschachtelte Mitgliedschaften zählen).
|
||
|
||
Prüfreihenfolge beim Login:
|
||
|
||
1. Enthält das ID-Token einen `groups`-Claim → Abgleich direkt im Token.
|
||
2. Sonst (oder bei Overage) → Microsoft Graph `checkMemberGroups`.
|
||
3. Schlägt beides fehl → **Anmeldung abgelehnt** (fail closed), Meldung „Gruppenmitgliedschaft konnte nicht geprüft werden“.
|
||
|
||
Leere Liste = keine Beschränkung.
|
||
|
||
**Ausgeschlossene Entra-Gruppen** (gleicher Tab, darunter): Mitglieder dieser Gruppen können sich **nie** per Microsoft
|
||
anmelden – auch wenn sie in einer erlaubten Gruppe sind (Ausschluss hat Vorrang, verschachtelte Mitgliedschaften zählen).
|
||
|
||
1. Steht eine ausgeschlossene Gruppe im `groups`-Claim → sofort abgelehnt.
|
||
2. Sonst wird **immer** Microsoft Graph gefragt (`checkMemberGroups`, Berechtigung `User.Read.All`), denn ein `groups`-Claim
|
||
kann in der App-Registrierung gefiltert sein und beweist nicht, dass jemand *kein* Mitglied ist.
|
||
3. Schlägt die Graph-Prüfung fehl → Anmeldung abgelehnt (fail closed).
|
||
|
||
Die Passwort-Anmeldung betrifft das nicht; wer auch die sperren will, kombiniert es mit dem Nur-Button-Modus.
|
||
|
||
### Nur-Button-Modus & Fallback
|
||
|
||
Im Tab *Sicherheit* → **Button-only mode**:
|
||
|
||
- Blendet Benutzername/Passwort-Felder aus. Der „Passwort vergessen?“-Link wird serverseitig entfernt, die
|
||
Passwort-vergessen-Seite leitet zur Anmeldung um, und Passwort-Resets (auch „Passwort zurücksetzen“ in der
|
||
Benutzerliste) sind gesperrt – mit aktivem Fallback-Link funktioniert beides wie gewohnt.
|
||
- **Sperrt Passwort-Logins serverseitig**, nicht nur per CSS – auf `wp-login.php` und in jedem eigenen Login-Formular (`authenticate`-Filter).
|
||
- Unterschieden wird nach **Zugangsdaten, nicht nach Anfrage-Typ**: Das normale Passwort wird überall abgelehnt – auch über
|
||
XML-RPC und in Login-Handlern anderer Plugins, die in `xmlrpc.php` oder einer REST-Anfrage laufen. Application Passwords
|
||
(REST, XML-RPC) und WP-CLI funktionieren weiter; API-Anfragen bekommen aber nie ein Login-Cookie.
|
||
- Falsches und richtiges Passwort erhalten dieselbe Meldung (kein Passwort-Orakel).
|
||
- Einzelne Ausnahmen per Filter `m365_login_block_password_login`.
|
||
- Bleibt aktiv, auch wenn die Verbindung zu Microsoft kaputtgeht (abgelaufenes Zertifikat, rotierte Salts) – dann gibt es
|
||
einen roten Hinweis im Backend, und nur der Fallback-Link oder die Konstante helfen. Passwort-Logins schalten sich nie
|
||
stillschweigend wieder ein.
|
||
|
||
**Fallback (Notausgang):** Beim Speichern erzeugt das Plugin einen geheimen Schlüssel und zeigt den Fallback-Link an:
|
||
|
||
```
|
||
https://deine-seite.tld/wp-login.php?m365_fallback=AbC…xYz
|
||
```
|
||
|
||
Wer den Link öffnet, sieht für **30 Minuten in diesem Browser** wieder das normale Formular und kann sich mit Passwort anmelden.
|
||
Der Schlüssel landet nicht im Cookie (nur ein HMAC davon), Fehlversuche werden pro IP gedrosselt (10 Versuche / 15 Minuten),
|
||
und über die Checkbox *Neuen Schlüssel beim Speichern erzeugen* lässt er sich jederzeit rotieren.
|
||
|
||
**Notschalter ohne Backend-Zugang:** In `wp-config.php`
|
||
|
||
```php
|
||
define( 'M365_LOGIN_DISABLE_BUTTON_ONLY', true );
|
||
```
|
||
|
||
schaltet den Modus komplett ab. Alternativ das Plugin-Verzeichnis per FTP umbenennen.
|
||
|
||
> ⚠️ Vor dem Aktivieren sicherstellen, dass dein eigenes Admin-Konto per Microsoft funktioniert, und den Fallback-Link sicher ablegen.
|
||
|
||
### Eigene Login-Seite
|
||
|
||
Das Plugin funktioniert auch, wenn die Anmeldung nicht über `wp-login.php` läuft.
|
||
|
||
| Baustein | Was passiert |
|
||
| --- | --- |
|
||
| **`wp_login_form()`** (Themes, viele Login-Plugins) | Der Button wird automatisch unter dem Formular eingefügt, inklusive Fehlermeldungen darüber. Im Nur-Button-Modus werden die Passwortfelder ausgeblendet. Abschaltbar im Tab *Button*. |
|
||
| **Shortcode** | `[m365_login_button redirect="/dashboard/" divider="yes" messages="yes"]` für Block-Editor und Page Builder (Elementor, Divi, …). |
|
||
| **Template-Funktion** | `m365_login_button( array( 'redirect' => '/dashboard/' ) );` in Theme-Dateien, `m365_login_messages();` für die Meldungen an anderer Stelle. |
|
||
| **URL der Login-Seite** (Tab *Button* → *Eigene Login-Seite*) | Fehlermeldungen nach einem gescheiterten Microsoft-Login, der Fallback-Link und die Weiterleitung nach dem Abmelden zeigen auf diese Seite statt auf `wp-login.php`. |
|
||
| **Nur-Button-Modus** | Greift serverseitig für alle Passwort-Logins, also auch für Formulare von Page Buildern, die das Plugin nicht ausblenden kann. |
|
||
|
||
Plugins, die `wp-login.php` umbenennen (z. B. WPS Hide Login), sind kompatibel, weil das Plugin durchgehend `wp_login_url()` verwendet.
|
||
|
||
### Benutzer-Sync
|
||
|
||
> **Privilegierte Konten** (Administratoren, Redakteure mit `unfiltered_html`, alle mit Rechten an Benutzern, Plugins
|
||
> oder Themes – auf irgendeiner Site des Netzwerks – sowie deaktivierte Konten, die solche Rollen zurückbekämen) werden
|
||
> nie über das frei setzbare `mail`-Attribut verknüpft. Siehe [Administrator-Konten verknüpfen](#administrator-konten-verknüpfen).
|
||
> Ihre E-Mail-Adresse ändert der Sync nie automatisch. Bei allen anderen Konten informiert WordPress die alte Adresse über
|
||
> eine Änderung.
|
||
|
||
Tab *Benutzer-Sync*. Legt WordPress-Konten für Microsoft-365-Benutzer an und hält sie aktuell – manuell per Knopfdruck,
|
||
automatisch per WP-Cron (stündlich, zweimal täglich, täglich) oder per WP-CLI.
|
||
|
||
**Welche Benutzer?** Ohne Auswahl alle Mitglieder des Tenants; optional nur Mitglieder bestimmter Gruppen
|
||
(verschachtelte Mitgliedschaften zählen). Gäste (B2B) nur auf Wunsch. Die Domain-Allowlist aus dem Tab *Sicherheit* gilt auch hier.
|
||
|
||
**Was passiert pro Benutzer?**
|
||
|
||
| Situation | Ergebnis |
|
||
| --- | --- |
|
||
| Kein WordPress-Konto vorhanden | Konto wird angelegt: Benutzername aus der E-Mail, Zufallspasswort, **keine E-Mail an den Benutzer**, Standardrolle + zugeordnete Rollen. Die Anmeldung läuft über den Microsoft-Button. |
|
||
| Konto mit derselben E-Mail (Mailadresse **oder** UPN) existiert schon | Wird mit der Microsoft-Objekt-ID verknüpft, Profilfelder werden aktualisiert. Rollen bleiben unangetastet, außer *„Auch die Rollen von Konten verwalten, die schon vor dem Sync existierten“* ist aktiv. |
|
||
| Bereits verknüpft | E-Mail-Adresse, Profilfelder, Profilbild und (bei importierten Konten) Rollen werden aktualisiert. |
|
||
| In Microsoft 365 **deaktiviert** | Wahlweise nichts tun, WordPress-Konto **deaktivieren** oder **löschen**. |
|
||
| In Microsoft 365 **gelöscht** | Wahlweise nichts tun, deaktivieren oder löschen. |
|
||
| Nicht mehr in den Sync-Gruppen | Wahlweise nichts tun, deaktivieren oder löschen. |
|
||
| Wieder aktiv in Microsoft 365 | Vom Sync deaktivierte Konten werden automatisch reaktiviert (von Hand deaktivierte nicht). |
|
||
|
||
**Rollen.** Jeder importierte Benutzer bekommt die *Standardrolle*. Darunter lassen sich Microsoft-365-Gruppen per Suche
|
||
auswählen und je einer WordPress-Rolle zuordnen (z. B. „Redaktion“ → Redakteur). Zwei Modi:
|
||
|
||
- *Zusätzlich zur Standardrolle* – der Benutzer hat danach mehrere Rollen.
|
||
- *Anstelle der Standardrolle* – die erste passende Gruppe der Liste gewinnt (Reihenfolge per ↑).
|
||
|
||
Verlässt jemand eine Gruppe, wird die Rolle beim nächsten Lauf entfernt. Die Rollen importierter Konten verwaltet der Sync
|
||
vollständig – manuelle Änderungen werden überschrieben.
|
||
|
||
**Profilfelder.** Frei wählbar: Anzeigename, Vor- und Nachname, Profilbild, Position, Abteilung, Firma, Büro, Personalnummer,
|
||
Telefon (geschäftlich/mobil), Adresse, Sprache. Namen landen in den normalen WordPress-Feldern, alles andere in User-Meta mit
|
||
dem Präfix `m365_` (z. B. `m365_department`) und wird auf der Profilseite angezeigt. Das **Profilbild** wird nach
|
||
`wp-content/uploads/m365-login-avatars/` geladen (Dateiname mit gesalzenem Hash statt Objekt-ID) und ersetzt überall den
|
||
Gravatar. Achtung: Avatare sind öffentlich sichtbar, wo WordPress sie anzeigt.
|
||
|
||
Microsoft 365 hat immer Vorrang, bei jedem Lauf:
|
||
|
||
| In Microsoft 365 … | … in WordPress |
|
||
| --- | --- |
|
||
| Feld geändert | Feld wird überschrieben. |
|
||
| Feld geleert | Feld wird geleert (`m365_*`-Meta gelöscht; ein leerer Anzeigename wird nicht übernommen). |
|
||
| Profilbild geändert | Neues Bild wird geladen, das alte gelöscht; die Avatar-URL ändert sich, damit Browser nicht das alte Bild zeigen. |
|
||
| Profilbild gelöscht | Bild wird gelöscht, der Avatar fällt auf Gravatar zurück. |
|
||
|
||
Die Bildversionen werden per Graph-`$batch` geprüft (20 Benutzer pro Anfrage); heruntergeladen werden nur geänderte Bilder,
|
||
höchstens 500 pro Lauf, der Rest folgt im nächsten (Filter `m365_login_sync_photo_limit`, Prüfintervall per
|
||
`m365_login_sync_photo_interval`). Bei einem Fehler von Microsoft bleibt das vorhandene Bild erhalten – gelöscht wird nur,
|
||
wenn Microsoft ausdrücklich „kein Foto“ meldet. **Abgewählte** Felder und Bilder werden beim nächsten Lauf aus den Profilen
|
||
entfernt (Vor-, Nach- und Anzeigename bleiben stehen); wird ein Benutzer in WordPress gelöscht, wird auch sein Bild gelöscht.
|
||
|
||
**Deaktivierte Konten** bekommen ein Zufallspasswort, verlieren ihre Rolle auf der Site (sie wird gemerkt und bei der
|
||
Reaktivierung zurückgegeben) und alle Application Passwords – so bleiben sie auch gesperrt, wenn das Plugin einmal
|
||
deaktiviert wird. Sie können sich überhaupt nicht mehr anmelden – weder per Microsoft noch per Passwort,
|
||
Anwendungspasswort oder bestehender Session (alle Sessions werden beendet). In der Benutzerliste zeigt die Spalte
|
||
*Microsoft 365* den Status; per Zeilenaktion lassen sich Konten auch von Hand deaktivieren und reaktivieren.
|
||
|
||
**Löschen** braucht einen Benutzer, der die Beiträge übernimmt. Ohne Auswahl wird stattdessen deaktiviert – es gehen nie Inhalte verloren.
|
||
|
||
**Schutzmechanismen**
|
||
|
||
- **Testlauf:** zeigt vollständig, was angelegt, geändert, deaktiviert oder gelöscht würde – ohne etwas zu ändern.
|
||
- **Sicherheitsstopp:** Würde ein Lauf mehr als 20 % der verknüpften Konten (mindestens 5) deaktivieren oder löschen,
|
||
passiert gar nichts (Filter `m365_login_sync_deprovision_limit`).
|
||
- **Fehler = Abbruch:** Schlägt eine Graph-Anfrage fehl, bricht der Lauf ab, bevor irgendein Konto deaktiviert wird.
|
||
„Gelöscht“ gilt ein Konto nur, wenn Graph für genau diese Objekt-ID `404` liefert.
|
||
- **Geschützte Konten:** Administratoren, die schon vor dem Sync existierten, und das eigene Konto werden nie
|
||
deaktiviert, gelöscht oder umgestuft (Filter `m365_login_sync_protect_user`).
|
||
- **Sperre gegen Parallelläufe**, Protokoll der letzten Ausführung im Backend.
|
||
|
||
**WP-CLI** – empfehlenswert für große Verzeichnisse oder exakte Zeiten per System-Cron:
|
||
|
||
```bash
|
||
wp m365-login sync --dry-run # Testlauf
|
||
wp m365-login sync # echter Lauf
|
||
```
|
||
|
||
### Administrator-Konten verknüpfen
|
||
|
||
Das `mail`-Attribut in Entra ID kann jeder Benutzer- oder Exchange-Administrator des Tenants frei setzen – wer es auf die
|
||
Adresse eines WordPress-Admins setzt, dürfte sonst dessen Konto übernehmen. Privilegierte Konten werden deshalb nur auf
|
||
einem dieser Wege mit einem Microsoft-Konto verknüpft (per Anmeldung oder Sync):
|
||
|
||
| Weg | Wann sinnvoll |
|
||
| --- | --- |
|
||
| **Selbst verknüpfen:** *Profil → Microsoft 365 → „Mit Microsoft-Konto verknüpfen“*. Die Person ist in WordPress angemeldet (beweist das WordPress-Konto) und meldet sich einmal bei Microsoft an (beweist das Microsoft-Konto). | Immer – auch wenn UPN und Mailadresse völlig verschieden sind. Im Nur-Button-Modus vorher über den Fallback-Link mit Passwort anmelden. |
|
||
| **Zuweisen:** Ein Administrator trägt beim Bearbeiten des Benutzers unter *Microsoft 365* den **UPN** ein (*Zugewiesenes Microsoft-Konto*). Anmeldung und Sync verknüpfen genau dieses Konto. | Mehrere Admins einrichten, ohne dass jeder selbst klicken muss. |
|
||
| **Automatisch:** UPN eines Mitglieds (kein Gast) = WordPress-E-Mail. | Wenn UPN und Mailadresse bei euch gleich sind. |
|
||
|
||
Nach der Verknüpfung findet die Anmeldung das Konto über die unveränderliche Objekt-ID – E-Mail-Adresse oder UPN dürfen sich
|
||
danach ändern. Eine bestehende Verknüpfung kann nur ein Administrator aufheben (*Verknüpfung mit dem Microsoft-Konto
|
||
aufheben* im Profil); eine Person kann ihr Konto nicht selbst auf ein anderes Microsoft-Konto umhängen.
|
||
|
||
---
|
||
|
||
## Sicherheitskonzept
|
||
|
||
| Bedrohung | Gegenmaßnahme |
|
||
| --- | --- |
|
||
| Abfangen von Tokens im Browser | Authorization Code Flow **mit PKCE (S256)**; ID-Token wird serverseitig geholt, `response_mode=query` ohne Token. |
|
||
| CSRF / Login-CSRF | `state` ist zufällig (256 Bit), **einmalig** verwendbar, 10 Min. gültig und per HttpOnly-/SameSite-Cookie an den startenden Browser gebunden. |
|
||
| Token-Replay | `nonce` wird im ID-Token geprüft und mit dem State-Datensatz verworfen. |
|
||
| Gefälschte Tokens | Signaturprüfung gegen Microsofts JWKS (`RS256` only; `alg=none`/HMAC werden abgelehnt), Schlüssel-Rollover wird automatisch nachgeladen. `iss`, `aud`, `tid`, `exp`, `nbf`, `iat` werden geprüft. |
|
||
| Fremde Tenants | Bei konfigurierter Tenant-GUID **Tenant-Pinning**; sonst Issuer-Konsistenz mit `tid`. |
|
||
| Kontoübernahme per E-Mail-Recycling | **Bindung an die Objekt-ID** (`oid`) beim ersten Login. |
|
||
| Unbefugte Konten | Der Login legt keine Konten an; optionale Domain-Allowlist, optionale Gruppen-Beschränkung (fail closed). Konten entstehen nur durch den explizit gestarteten bzw. aktivierten Benutzer-Sync. |
|
||
| Ausgeschiedene Mitarbeitende | Benutzer-Sync deaktiviert oder löscht Konten, die in Microsoft 365 deaktiviert/gelöscht wurden; deaktivierte Konten verlieren sofort alle Sessions und jeden Anmeldeweg. |
|
||
| Massen-Deprovisionierung durch Fehlkonfiguration | Testlauf, Sicherheitsstopp (> 20 % / min. 5), Abbruch bei jedem Graph-Fehler, „gelöscht“ nur bei `404` für die konkrete Objekt-ID, geschützte Administratoren. |
|
||
| Secret-Diebstahl aus der Datenbank | AES-256-GCM, Schlüssel per HKDF aus `AUTH_KEY`/`SECURE_AUTH_KEY`; ohne `wp-config.php` ist der Datensatz wertlos. Gilt für Client Secret und privaten Zertifikatsschlüssel. |
|
||
| Secret-Abfluss im Transport | Zertifikatsmodus: es wird nie ein Geheimnis übertragen, nur eine 5 Minuten gültige, signierte Client Assertion (RFC 7523). |
|
||
| Kontoübernahme im Multi-Tenant-Modus | `email`-Claim fremder Tenants wird ignoriert (nur UPN mit verifizierter Domain oder `xms_edov`). |
|
||
| Flooding der State-Tabelle | Max. 300 Login-Starts pro IP und 10 Minuten; Proxy-Header per `M365_LOGIN_CLIENT_IP_HEADER` – am besten ein einwertiger Header wie `HTTP_CF_CONNECTING_IP` oder `HTTP_X_REAL_IP` (bei `X-Forwarded-For` zählt der rechte, vom Proxy geschriebene Eintrag). |
|
||
| Offene Redirects | `redirect_to` läuft durch `wp_validate_redirect`, alle Redirects über `wp_safe_redirect`. |
|
||
| Fehler-Reflektion | Fehlermeldungen sind Codes → feste, übersetzte Texte; Details nur ins Log (`WP_DEBUG_LOG`). |
|
||
| Rate Limiting Fallback-Key | 10 Fehlversuche pro IP / 15 Min. |
|
||
|
||
Die Klassen für JWT-Prüfung, Verschlüsselung, Zertifikate und die Login-Sperre haben isolierte Tests (manipulierte Signaturen, abgelaufene Tokens, falsche Audience/Tenant/Issuer, `alg=none`, fremde Schlüssel, gefälschte Fallback-Cookies, schwache RSA-Schlüssel).
|
||
Der vollständige Bericht mit Bedrohungsmodell, Befunden und Betriebsempfehlungen: **[docs/security-audit.md](docs/security-audit.md)**.
|
||
|
||
---
|
||
|
||
## Shortcode & Hooks
|
||
|
||
**Shortcode** für eigene Login-Seiten:
|
||
|
||
```
|
||
[m365_login_button redirect="/mein-konto/"]
|
||
```
|
||
|
||
**Filter & Actions**
|
||
|
||
```php
|
||
// Button z. B. nur im Intranet zeigen
|
||
add_filter( 'm365_login_show_button', function ( $show ) {
|
||
return $show && 'intranet.example.com' === $_SERVER['HTTP_HOST'];
|
||
} );
|
||
|
||
// domain_hint mitschicken, damit Microsoft direkt die Firmenanmeldung zeigt
|
||
add_filter( 'm365_login_authorize_params', function ( $params ) {
|
||
$params['domain_hint'] = 'contoso.com';
|
||
return $params;
|
||
} );
|
||
|
||
// E-Mail vor dem Lookup umschreiben (z. B. Alias-Domain)
|
||
add_filter( 'm365_login_match_email', function ( $email, $claims ) {
|
||
return str_replace( '@alt.contoso.com', '@contoso.com', $email );
|
||
}, 10, 2 );
|
||
|
||
// Eigene Zusatzprüfung nach allen Plugin-Checks
|
||
add_filter( 'm365_login_allow_user', function ( $allowed, WP_User $user, array $claims ) {
|
||
return $allowed && ! in_array( 'subscriber', $user->roles, true );
|
||
}, 10, 3 );
|
||
|
||
// Nach erfolgreichem Login, z. B. Anzeigenamen synchronisieren
|
||
add_action( 'm365_login_success', function ( WP_User $user, array $claims ) {
|
||
if ( ! empty( $claims['name'] ) ) {
|
||
wp_update_user( array( 'ID' => $user->ID, 'display_name' => $claims['name'] ) );
|
||
}
|
||
}, 10, 2 );
|
||
|
||
// Ein vertrauenswürdiges Plugin darf trotz Nur-Button-Modus per Passwort anmelden
|
||
add_filter( 'm365_login_block_password_login', function ( $block, WP_User $user ) {
|
||
return doing_action( 'my_membership_plugin_login' ) ? false : $block;
|
||
}, 10, 2 );
|
||
|
||
// Redirect-URI anpassen (z. B. hinter einem Reverse Proxy)
|
||
add_filter( 'm365_login_redirect_uri', fn( $uri ) => 'https://www.example.com/m365-login/callback' );
|
||
```
|
||
|
||
**Benutzer-Sync**
|
||
|
||
```php
|
||
// Weiteres Graph-Attribut anbieten (landet in User-Meta "m365_cost_center")
|
||
add_filter( 'm365_login_sync_attributes', function ( $attributes ) {
|
||
$attributes['costCenter'] = array( 'label' => 'Kostenstelle', 'target' => 'm365_cost_center' );
|
||
return $attributes;
|
||
} );
|
||
|
||
// Rollen pro Person anpassen (erste Rolle = Hauptrolle)
|
||
add_filter( 'm365_login_sync_roles', function ( array $roles, $oid ) {
|
||
return $roles;
|
||
}, 10, 2 );
|
||
|
||
// Daten für neu angelegte Konten (Argumente für wp_insert_user)
|
||
add_filter( 'm365_login_sync_new_user_data', function ( array $data, array $person ) {
|
||
$data['user_login'] = strtolower( $person['userPrincipalName'] );
|
||
return $data;
|
||
}, 10, 2 );
|
||
|
||
// Weitere Konten vom Sync ausnehmen
|
||
add_filter( 'm365_login_sync_protect_user', function ( $protected, WP_User $user ) {
|
||
return $protected || in_array( 'shop_manager', $user->roles, true );
|
||
}, 10, 2 );
|
||
|
||
// Sicherheitsstopp anheben (Standard: 20 % der verknüpften Konten, mindestens 5)
|
||
add_filter( 'm365_login_sync_deprovision_limit', fn( $limit, $linked ) => max( 10, $linked * 0.5 ), 10, 2 );
|
||
|
||
// Weitere: m365_login_sync_email, m365_login_sync_photo_limit, m365_login_sync_photo_interval, m365_login_is_privileged_user
|
||
// Actions: m365_login_sync_user_created, m365_login_sync_finished, m365_login_user_disabled, m365_login_user_enabled
|
||
```
|
||
|
||
---
|
||
|
||
## Fehlerbehebung
|
||
|
||
| Meldung auf der Login-Seite | Ursache & Lösung |
|
||
| --- | --- |
|
||
| *Microsoft login is not configured yet.* | Tenant-ID, Client-ID oder Secret fehlt. |
|
||
| *The login request expired or was invalid.* | State abgelaufen (> 10 Min.), Cookie blockiert oder Seite doppelt geladen. Erneut versuchen; Cookies für die Domain erlauben. |
|
||
| *Could not complete the sign-in with Microsoft.* | Token-Tausch fehlgeschlagen – meist falsches/abgelaufenes Client Secret oder Redirect-URI stimmt nicht exakt mit Entra überein. Details im Log. |
|
||
| *The Microsoft sign-in could not be verified.* | ID-Token abgelehnt (Tenant, Audience, Signatur). Tenant-ID prüfen; Serverzeit prüfen (NTP). |
|
||
| *Your Microsoft account did not provide an e-mail address.* | `email`-Claim fehlt und UPN-Fallback ist aus oder UPN ist keine E-Mail. Claim in der Tokenkonfiguration hinzufügen. |
|
||
| *No WordPress account exists for your Microsoft e-mail address.* | E-Mail in WordPress stimmt nicht mit Microsoft überein. |
|
||
| *This WordPress account is linked to a different Microsoft account.* | Objekt-ID weicht ab. Wenn gewollt (neues Microsoft-Konto): User-Meta `_m365_login_oid` beim Benutzer löschen. |
|
||
| *… not a member of a group that is allowed …* | Benutzer ist in keiner der ausgewählten Gruppen. |
|
||
| *Your group membership could not be verified.* | Graph nicht erreichbar oder Berechtigung fehlt (`User.Read.All`) – oder `groups`-Claim aktivieren. Bei ausgeschlossenen Gruppen ist Graph immer nötig. |
|
||
| *… member of a group that is not allowed to sign in here.* | Benutzer ist Mitglied einer ausgeschlossenen Gruppe (auch verschachtelt). |
|
||
| *This account has been deactivated.* | Das Konto wurde vom Benutzer-Sync oder von Hand deaktiviert. *Benutzer → Zeilenaktion „Reaktivieren“* – ist die Person in Microsoft 365 noch deaktiviert, deaktiviert der nächste Sync sie wieder. |
|
||
|
||
| Meldung im Sync-Protokoll | Ursache & Lösung |
|
||
| --- | --- |
|
||
| *Microsoft Graph refused the request …* | Anwendungsberechtigung `User.Read.All` (und bei Gruppen `GroupMember.Read.All`) fehlt oder keine Administratorzustimmung. |
|
||
| *The user sync needs a pinned tenant ID …* | Im Tab *Verbindung* die Tenant-GUID statt `organizations`/`common` eintragen. |
|
||
| *Safety stop: … accounts would be deactivated or deleted …* | Sync-Gruppen oder Tenant prüfen, Testlauf ansehen; bei gewollter Massenänderung das Limit per Filter anheben. |
|
||
| *… is protected … and was not changed.* | Bestehender Administrator oder eigenes Konto – bewusst ausgenommen. |
|
||
| Zeitüberschreitung beim Klick auf *Jetzt synchronisieren* | Der Lauf geht serverseitig weiter; Seite später neu laden. Für große Verzeichnisse `wp m365-login sync` verwenden. |
|
||
|
||
Logging: Mit `WP_DEBUG` und `WP_DEBUG_LOG` schreibt das Plugin Fehlerdetails mit Präfix `[M365 Login]` nach `wp-content/debug.log`. Es werden nie Tokens oder Secrets geloggt.
|
||
|
||
---
|
||
|
||
## Entwicklung
|
||
|
||
```
|
||
m365-login.php Plugin-Header & Bootstrap
|
||
includes/
|
||
class-m365-login.php Verdrahtung der Komponenten
|
||
class-m365-login-settings.php Defaults, Sanitizing, Redirect-URI, Fallback-Key
|
||
class-m365-login-crypto.php AES-256-GCM für das Client Secret
|
||
class-m365-login-jwt.php RS256-Verifikation, JWKS → PEM
|
||
class-m365-login-auth.php OAuth-Flow, Callback, Benutzerzuordnung, Nur-Button-Modus
|
||
class-m365-login-graph.php Client-Credentials-Token, Paging, Benutzer/Gruppen/Fotos, checkMemberGroups
|
||
class-m365-login-sync.php Benutzer-Sync, Rollen, Profilfelder/-bilder, Deaktivierung, WP-CLI
|
||
class-m365-login-button.php Ausgabe auf wp-login.php, Shortcode
|
||
class-m365-login-admin.php Einstellungsseite, AJAX
|
||
assets/ CSS/JS für Login-Seite und Backend (unminifiziert)
|
||
languages/ .pot, de_DE, de_DE_formal
|
||
bin/ build-zip.sh, make-pot.py, compile-mo.py
|
||
docs/ Einreichungs-Checkliste
|
||
```
|
||
|
||
```bash
|
||
composer install # PHPCS + WordPress Coding Standards + PHPCompatibility
|
||
composer lint # php -l für alle Dateien
|
||
composer phpcs # Coding-Standards-Prüfung (phpcs.xml.dist)
|
||
python3 bin/make-pot.py # Strings extrahieren (oder: wp i18n make-pot . languages/m365-login.pot)
|
||
python3 bin/compile-mo.py # .po → .mo
|
||
bash bin/build-zip.sh # build/m365-login.zip
|
||
```
|
||
|
||
Die CI (`.github/workflows/ci.yml`) prüft Syntax unter PHP 7.4–8.4, führt PHPCS aus und lässt den offiziellen
|
||
**WordPress Plugin Check** über das Build-Verzeichnis laufen.
|
||
|
||
---
|
||
|
||
## Einreichung bei WordPress.org
|
||
|
||
Das Plugin bringt alles mit, was das Review-Team verlangt: `readme.txt` mit *External services*-Abschnitt, GPL-Lizenz,
|
||
`uninstall.php`, eindeutige Präfixe, keine externen Assets, Übersetzungen, Verzeichnis-Icon. Die komplette Checkliste
|
||
(inkl. Slug-/Marken-Hinweisen und SVN-Schritten nach der Freigabe) steht in
|
||
**[docs/wordpress-org-einreichung.md](docs/wordpress-org-einreichung.md)**.
|
||
|
||
---
|
||
|
||
## FAQ
|
||
|
||
<details>
|
||
<summary><strong>Kann ich Benutzer automatisch anlegen lassen?</strong></summary>
|
||
Ja, mit dem <a href="#benutzer-sync">Benutzer-Sync</a>: Er importiert alle (oder ausgewählte) Microsoft-365-Benutzer vorab als WordPress-Konten. Der Login selbst legt weiterhin nie Konten an – wer nicht importiert oder von Hand angelegt wurde, kommt nicht rein.
|
||
</details>
|
||
|
||
<details>
|
||
<summary><strong>Funktioniert es mit privaten Microsoft-Konten (outlook.com)?</strong></summary>
|
||
Ja, Tenant auf <code>consumers</code> oder <code>common</code> stellen. Microsoft erlaubt dann keine Query-Strings in Redirect-URIs, deshalb müssen sprechende Permalinks aktiv sein (Callback ohne <code>?</code>).
|
||
</details>
|
||
|
||
<details>
|
||
<summary><strong>Multisite?</strong></summary>
|
||
Ja. Einstellungen gelten pro Site, dürfen aber **nur von Super-Admins** geändert werden: Sie entscheiden, welche
|
||
Microsoft-Identität sich als welcher (netzwerkweite) WordPress-Benutzer anmelden darf. Ein Site-Admin könnte sonst einen
|
||
eigenen Tenant eintragen und sich als Super-Admin anmelden. Der Benutzer muss Mitglied der Site (oder Super-Admin) sein.
|
||
</details>
|
||
|
||
<details>
|
||
<summary><strong>Was passiert beim Deinstallieren?</strong></summary>
|
||
Einstellungen, Caches (Transients), Sync-Protokoll, Cron-Termin, gespeicherte Profilbilder und die pro Benutzer gespeicherten Plugin-Daten (Objekt-ID, Deaktivierungs-Status) werden entfernt – auch in Multisite. Importierte Konten und übernommene Profilfelder (<code>m365_*</code>) bleiben erhalten. Deaktivierte Konten bleiben ohne Rolle, mit Zufallspasswort und ohne Application Passwords – der Deaktivierungs-Vermerk selbst wird entfernt.
|
||
</details>
|
||
|
||
<details>
|
||
<summary><strong>Benutzer-Sync und Multisite?</strong></summary>
|
||
Der Sync arbeitet pro Site: Neue Konten werden zur aktuellen Site hinzugefügt, „Löschen“ entfernt das Konto nur aus dieser Site. Die Deaktivierung gilt netzwerkweit, weil sie am Benutzer hängt.
|
||
</details>
|
||
|
||
<details>
|
||
<summary><strong>Ich habe mich ausgesperrt.</strong></summary>
|
||
Fallback-Link öffnen. Kein Link zur Hand? <code>define( 'M365_LOGIN_DISABLE_BUTTON_ONLY', true );</code> in die <code>wp-config.php</code> oder den Plugin-Ordner per FTP umbenennen.
|
||
</details>
|
||
|
||
---
|
||
|
||
## Lizenz
|
||
|
||
GPL-2.0-or-later – siehe [LICENSE](LICENSE). „Microsoft“, „Microsoft 365“ und das Microsoft-Logo sind Marken der Microsoft Corporation;
|
||
das Plugin ist ein unabhängiges Community-Projekt und steht in keiner Verbindung zu Microsoft.
|