diff --git a/CHANGELOG.md b/CHANGELOG.md index 24864d8..c6863e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,10 +19,13 @@ All notable changes to this project are documented in this file. The format foll - Graph client: paging, retry on throttling (429/503/504), user, group member and photo endpoints. - Filters and actions for the sync (`m365_login_sync_*`, `m365_login_user_disabled`, `m365_login_user_enabled`). +- Excluded Entra groups (Security tab): members can never sign in with Microsoft, even when they are in an allowed group. Checked via the `groups` claim and always via Microsoft Graph `checkMemberGroups` (a filtered claim cannot prove non-membership); fails closed. + ### Changed - The group picker is reusable (security groups, sync groups, role mapping). ### Fixed +- Failed Microsoft sign-ins (token exchange, token verification, object ID mismatch, group checks) ended in a PHP fatal error because the auth component's log helper had been removed in 1.0.0 development. - "Generate certificate" and removing the certificate did not keep the change and encrypted a stored client secret a second time (internal settings writes ran through the form sanitiser). ## [1.0.0] – 2026-09-22 diff --git a/README.md b/README.md index ee0f185..9020155 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ | 📧 **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. Gruppen werden direkt im Backend gesucht und ausgewählt. | +| 👥 **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). | @@ -243,6 +243,16 @@ Prüfreihenfolge beim Login: 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**: @@ -482,7 +492,8 @@ add_filter( 'm365_login_sync_deprovision_limit', fn( $limit, $linked ) => max( 1 | *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. | +| *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 | diff --git a/docs/security-audit.md b/docs/security-audit.md index 53b4344..50eb2c3 100644 --- a/docs/security-audit.md +++ b/docs/security-audit.md @@ -137,6 +137,15 @@ Der Sync legt Konten an, ändert Rollen und deaktiviert bzw. löscht Konten. Gep - **Graph-Aufrufe:** nur `https://graph.microsoft.com/v1.0/`; Paging-Links werden auf diesen Präfix geprüft, IDs sind GUIDs. - **Ausgabe:** Protokoll und Profilfelder werden escaped ausgegeben; Benutzer-Zeilenaktionen mit Nonce und `edit_user`. +### N-8 · Ausgeschlossene Gruppen und fehlende Log-Methode (1.1.0) — **neu / behoben** + +- **Ausgeschlossene Entra-Gruppen:** Ausschluss vor Erlaubnis; Treffer im `groups`-Claim lehnt sofort ab, ohne Treffer wird + immer Graph `checkMemberGroups` (transitiv) gefragt, weil ein gefilterter Claim Nicht-Mitgliedschaft nicht beweist; + Graph-Fehler → Ablehnung (fail closed). +- **Behoben:** `M365_Login_Auth::log()` fehlte seit der Umstellung auf eigene Login-Seiten; alle Fehlerpfade des Callbacks + endeten in einem PHP-Fatal-Error (kein Sicherheitsleck – die Anmeldung scheiterte –, aber keine Fehlermeldung und ein + 500er). Durch Tests in einer echten Installation gefunden. + Hinweis für den Betrieb: Personenbezogene Daten (Telefon, Adresse, Foto) nur synchronisieren, wenn sie auf der Website gebraucht werden; die Auswahl ist bewusst Opt-in (Standard: nur Namen). diff --git a/includes/class-m365-login-admin.php b/includes/class-m365-login-admin.php index 6c6ef67..cbfff4d 100644 --- a/includes/class-m365-login-admin.php +++ b/includes/class-m365-login-admin.php @@ -1081,6 +1081,16 @@ class M365_Login_Admin { +
+

+

+ + group_picker( 'denied_groups', $this->settings->denied_groups(), 'chips', __( 'No groups excluded.', 'm365-login' ) ); ?> +
+

+
+
+

diff --git a/includes/class-m365-login-auth.php b/includes/class-m365-login-auth.php index 810e99a..56824b9 100644 --- a/includes/class-m365-login-auth.php +++ b/includes/class-m365-login-auth.php @@ -642,6 +642,75 @@ class M365_Login_Auth { return $body; } + /** + * Applies the Entra group rules: members of an excluded group are refused, + * everybody else needs membership in one of the allowed groups (if any are set). + * + * @param array $claims Verified claims. + * @param string $oid User object ID. + * @return true|string True, or an error code for fail(). + */ + private function check_groups( $claims, $oid ) { + $denied = $this->check_denied_groups( $claims, $oid ); + if ( true !== $denied ) { + return $denied; + } + return $this->check_allowed_groups( $claims, $oid ); + } + + /** + * Group IDs from the "groups" claim, or null when the token has no complete list (claim missing or overage). + * + * @param array $claims Verified claims. + * @return string[]|null + */ + private function token_groups( $claims ) { + $overage = ! empty( $claims['_claim_names'] ) || ! empty( $claims['hasgroups'] ); + if ( $overage || ! isset( $claims['groups'] ) || ! is_array( $claims['groups'] ) ) { + return null; + } + return array_map( 'strtolower', array_filter( $claims['groups'], 'is_string' ) ); + } + + /** + * Refuses members of an excluded group (fails closed). + * + * A "groups" claim can be filtered in the app registration (e.g. only groups assigned to the + * application), so it can prove membership but never non-membership: without a match in the + * token the plugin always asks Microsoft Graph. + * + * @param array $claims Verified claims. + * @param string $oid User object ID. + * @return true|string True, or an error code for fail(). + */ + private function check_denied_groups( $claims, $oid ) { + $denied = array_keys( $this->settings->denied_groups() ); + if ( empty( $denied ) ) { + return true; + } + + $token_groups = $this->token_groups( $claims ); + if ( null !== $token_groups && array_intersect( $denied, $token_groups ) ) { + $this->log( 'User is a member of an excluded group (token claim).' ); + return 'in_denied_group'; + } + + if ( '' === $oid || ! M365_Login_Settings::is_guid( $oid ) ) { + return 'invalid_token'; + } + + $matches = $this->graph->check_member_groups( $oid, $denied ); + if ( is_wp_error( $matches ) ) { + $this->log( 'Excluded-group check via Microsoft Graph failed: ' . $matches->get_error_message() ); + return 'group_check_failed'; + } + if ( ! empty( $matches ) ) { + $this->log( 'User is a member of an excluded group (Graph).' ); + return 'in_denied_group'; + } + return true; + } + /** * Verifies membership in one of the allowed Entra groups. * @@ -652,15 +721,14 @@ class M365_Login_Auth { * @param string $oid User object ID. * @return true|string True, or an error code for fail(). */ - private function check_groups( $claims, $oid ) { + private function check_allowed_groups( $claims, $oid ) { $allowed = array_keys( $this->settings->allowed_groups() ); if ( empty( $allowed ) ) { return true; } - $overage = ! empty( $claims['_claim_names'] ) || ! empty( $claims['hasgroups'] ); - if ( ! $overage && isset( $claims['groups'] ) && is_array( $claims['groups'] ) ) { - $token_groups = array_map( 'strtolower', array_filter( $claims['groups'], 'is_string' ) ); + $token_groups = $this->token_groups( $claims ); + if ( null !== $token_groups ) { if ( array_intersect( $allowed, $token_groups ) ) { return true; } @@ -796,6 +864,17 @@ class M365_Login_Auth { ); } + /** + * Writes a diagnostic line to the debug log (only with WP_DEBUG and WP_DEBUG_LOG; never tokens or secrets). + * + * @param string $message Message. + */ + private function log( $message ) { + if ( defined( 'WP_DEBUG' ) && WP_DEBUG && defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) { + error_log( '[M365 Login] ' . $message ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log + } + } + /** * Aborts the flow and shows a generic error on the login screen. * @@ -858,6 +937,7 @@ class M365_Login_Auth { 'not_allowed' => __( 'You are not allowed to sign in with this account.', 'm365-login' ), 'not_in_group' => __( 'Your Microsoft account is not a member of a group that is allowed to sign in here.', 'm365-login' ), 'group_check_failed' => __( 'Your group membership could not be verified. Please contact an administrator.', 'm365-login' ), + 'in_denied_group' => __( 'Your Microsoft account is a member of a group that is not allowed to sign in here.', 'm365-login' ), 'fallback_invalid' => __( 'The fallback key is not valid.', 'm365-login' ), 'fallback_locked' => __( 'Too many attempts. Please wait 15 minutes.', 'm365-login' ), 'too_many_attempts' => __( 'Too many sign-in attempts from your connection. Please wait a few minutes and try again.', 'm365-login' ), diff --git a/includes/class-m365-login-settings.php b/includes/class-m365-login-settings.php index 5852f00..566450b 100644 --- a/includes/class-m365-login-settings.php +++ b/includes/class-m365-login-settings.php @@ -46,6 +46,7 @@ class M365_Login_Settings { 'bind_oid' => 1, 'allowed_domains' => '', 'allowed_groups' => array(), // id => display name. + 'denied_groups' => array(), // id => display name; members may never sign in. 'remember_me' => 0, // Button-only mode. 'button_only' => 0, @@ -333,6 +334,15 @@ class M365_Login_Settings { return self::guid_map( $this->get( 'allowed_groups', array() ) ); } + /** + * Excluded Entra group IDs (lowercase GUIDs) mapped to display names. + * + * @return array + */ + public function denied_groups() { + return self::guid_map( $this->get( 'denied_groups', array() ) ); + } + /** * Groups that limit the user sync (lowercase GUID => name); empty = whole tenant. * @@ -552,6 +562,7 @@ class M365_Login_Settings { // Allowed groups: GUID => name. $out['allowed_groups'] = self::sanitize_group_list( isset( $input['allowed_groups'] ) ? $input['allowed_groups'] : array() ); + $out['denied_groups'] = self::sanitize_group_list( isset( $input['denied_groups'] ) ? $input['denied_groups'] : array() ); // Button-only mode + fallback key. $out['button_only'] = empty( $input['button_only'] ) ? 0 : 1; diff --git a/languages/m365-login-de_DE.mo b/languages/m365-login-de_DE.mo index 982d310..efb30b8 100644 Binary files a/languages/m365-login-de_DE.mo and b/languages/m365-login-de_DE.mo differ diff --git a/languages/m365-login-de_DE.po b/languages/m365-login-de_DE.po index 4efe142..a358e8c 100644 --- a/languages/m365-login-de_DE.po +++ b/languages/m365-login-de_DE.po @@ -56,7 +56,7 @@ msgstr "Dieses Icon verwenden" msgid "Copied!" msgstr "Kopiert!" -#: includes/class-m365-login-admin.php:233 includes/class-m365-login-admin.php:848 includes/class-m365-login-admin.php:1102 includes/class-m365-login-admin.php:1147 +#: includes/class-m365-login-admin.php:233 includes/class-m365-login-admin.php:848 includes/class-m365-login-admin.php:1112 includes/class-m365-login-admin.php:1157 msgid "Copy" msgstr "Kopieren" @@ -914,136 +914,152 @@ msgid "Membership is read from the \"groups\" claim of the ID token when present msgstr "Die Mitgliedschaft wird aus dem „groups“-Claim des ID-Tokens gelesen, falls vorhanden; andernfalls fragt das Plugin Microsoft Graph (Anwendungsberechtigung „User.Read.All“ oder „Directory.Read.All“). Funktioniert beides nicht, wird die Anmeldung abgelehnt." #: includes/class-m365-login-admin.php:1085 +msgid "Excluded Entra groups (optional)" +msgstr "Ausgeschlossene Entra-Gruppen (optional)" + +#: includes/class-m365-login-admin.php:1086 +msgid "Members of these groups can never sign in with Microsoft – even if they are in an allowed group. Nested memberships count." +msgstr "Mitglieder dieser Gruppen können sich nie per Microsoft anmelden – auch nicht, wenn sie in einer erlaubten Gruppe sind. Verschachtelte Mitgliedschaften zählen." + +#: includes/class-m365-login-admin.php:1088 +msgid "No groups excluded." +msgstr "Keine Gruppen ausgeschlossen." + +#: includes/class-m365-login-admin.php:1090 +msgid "The plugin asks Microsoft Graph on every sign-in (application permission \"User.Read.All\" or \"Directory.Read.All\"), because a \"groups\" claim may be filtered and cannot prove that someone is not a member. If the check fails, the sign-in is refused. Password sign-in is not affected – combine with button-only mode if needed." +msgstr "Das Plugin fragt bei jeder Anmeldung Microsoft Graph (Anwendungsberechtigung „User.Read.All“ oder „Directory.Read.All“), weil ein „groups“-Claim gefiltert sein kann und nicht beweist, dass jemand kein Mitglied ist. Schlägt die Prüfung fehl, wird die Anmeldung abgelehnt. Die Passwort-Anmeldung ist nicht betroffen – bei Bedarf mit dem Nur-Button-Modus kombinieren." + +#: includes/class-m365-login-admin.php:1095 msgid "Button-only mode" msgstr "Nur-Button-Modus" -#: includes/class-m365-login-admin.php:1086 +#: includes/class-m365-login-admin.php:1096 msgid "Hides the username/password fields (on wp-login.php and in wp_login_form() forms) and refuses every interactive password sign-in on the site, including custom login forms. Application passwords, REST, XML-RPC and WP-CLI are not affected." msgstr "Blendet die Benutzername/Passwort-Felder aus (auf wp-login.php und in wp_login_form()-Formularen) und lehnt jede interaktive Passwort-Anmeldung auf der Website ab, auch in eigenen Login-Formularen. Anwendungspasswörter, REST, XML-RPC und WP-CLI sind nicht betroffen." -#: includes/class-m365-login-admin.php:1091 +#: includes/class-m365-login-admin.php:1101 msgid "Show only the Microsoft button on the login page" msgstr "Auf der Login-Seite nur den Microsoft-Button anzeigen" -#: includes/class-m365-login-admin.php:1092 +#: includes/class-m365-login-admin.php:1102 msgid "Becomes active once the connection is configured. Make sure your own account can sign in via Microsoft before enabling this." msgstr "Wird aktiv, sobald die Verbindung eingerichtet ist. Vor dem Aktivieren sicherstellen, dass das eigene Konto sich per Microsoft anmelden kann." -#: includes/class-m365-login-admin.php:1097 +#: includes/class-m365-login-admin.php:1107 msgid "Fallback link (keep it secret)" msgstr "Fallback-Link (geheim halten)" -#: includes/class-m365-login-admin.php:1098 +#: includes/class-m365-login-admin.php:1108 msgid "Opening this link shows the password form again in that browser for 30 minutes and allows password sign-in there. Bookmark it somewhere safe – it is your way back in if Microsoft sign-in ever breaks." msgstr "Wer diesen Link öffnet, sieht in diesem Browser 30 Minuten lang wieder das Passwort-Formular und kann sich dort mit Passwort anmelden. Sicher aufbewahren – er ist der Weg zurück, falls die Microsoft-Anmeldung einmal nicht funktioniert." -#: includes/class-m365-login-admin.php:1106 +#: includes/class-m365-login-admin.php:1116 msgid "Generate a new key when saving" msgstr "Beim Speichern einen neuen Schlüssel erzeugen" -#: includes/class-m365-login-admin.php:1109 +#: includes/class-m365-login-admin.php:1119 msgid "A key is generated automatically the first time you save these settings." msgstr "Beim ersten Speichern dieser Einstellungen wird automatisch ein Schlüssel erzeugt." #. translators: %s: PHP constant -#: includes/class-m365-login-admin.php:1115 +#: includes/class-m365-login-admin.php:1125 msgid "Emergency switch: add %s to wp-config.php to disable button-only mode entirely." msgstr "Notschalter: %s in die wp-config.php eintragen, um den Nur-Button-Modus vollständig abzuschalten." #. translators: %s: PHP constant -#: includes/class-m365-login-admin.php:1124 +#: includes/class-m365-login-admin.php:1134 msgid "What the plugin does to keep sign-ins safe" msgstr "So schützt das Plugin die Anmeldung" -#: includes/class-m365-login-admin.php:1126 +#: includes/class-m365-login-admin.php:1136 msgid "OpenID Connect authorization code flow with PKCE (S256) – no tokens ever pass through the browser." msgstr "OpenID Connect Authorization Code Flow mit PKCE (S256) – Tokens laufen nie durch den Browser." -#: includes/class-m365-login-admin.php:1127 +#: includes/class-m365-login-admin.php:1137 msgid "Single-use state and nonce values bound to the browser via an HttpOnly cookie (CSRF and replay protection)." msgstr "Einmalige State- und Nonce-Werte, per HttpOnly-Cookie an den Browser gebunden (CSRF- und Replay-Schutz)." -#: includes/class-m365-login-admin.php:1128 +#: includes/class-m365-login-admin.php:1138 msgid "ID token signature verified against Microsoft’s published signing keys; issuer, audience, tenant, expiry and nonce are checked." msgstr "Signatur des ID-Tokens wird gegen Microsofts veröffentlichte Signaturschlüssel geprüft; Issuer, Audience, Tenant, Ablauf und Nonce werden kontrolliert." -#: includes/class-m365-login-admin.php:1129 +#: includes/class-m365-login-admin.php:1139 msgid "Client secret encrypted at rest; sign-in never creates accounts or changes passwords." msgstr "Client Secret verschlüsselt gespeichert; die Anmeldung legt nie Konten an und ändert keine Passwörter." -#: includes/class-m365-login-admin.php:1137 +#: includes/class-m365-login-admin.php:1147 msgid "Save changes" msgstr "Änderungen speichern" -#: includes/class-m365-login-admin.php:1143 +#: includes/class-m365-login-admin.php:1153 msgid "Redirect URI" msgstr "Umleitungs-URI (Redirect URI)" -#: includes/class-m365-login-admin.php:1144 +#: includes/class-m365-login-admin.php:1154 msgid "Register this URI in your app registration under Authentication → Web → Redirect URIs:" msgstr "Diese URI in der App-Registrierung unter Authentifizierung → Web → Umleitungs-URIs eintragen:" -#: includes/class-m365-login-admin.php:1150 +#: includes/class-m365-login-admin.php:1160 msgid "Plain permalinks are active, so the callback uses a query string. If you enable pretty permalinks later, the redirect URI changes and must be updated in Entra ID." msgstr "Einfache Permalinks sind aktiv, daher verwendet der Callback einen Query-String. Werden später sprechende Permalinks aktiviert, ändert sich die Umleitungs-URI und muss in Entra ID angepasst werden." -#: includes/class-m365-login-admin.php:1153 +#: includes/class-m365-login-admin.php:1163 msgid "Your site does not use HTTPS. Microsoft only accepts http:// redirect URIs for localhost; production sites must use HTTPS." msgstr "Diese Website nutzt kein HTTPS. Microsoft akzeptiert http://-Umleitungs-URIs nur für localhost; produktive Websites benötigen HTTPS." -#: includes/class-m365-login-admin.php:1158 +#: includes/class-m365-login-admin.php:1168 msgid "Setup guide: app registration" msgstr "Anleitung: App-Registrierung" -#: includes/class-m365-login-admin.php:1160 +#: includes/class-m365-login-admin.php:1170 msgid "Open entra.microsoft.com → Identity → Applications → App registrations → New registration." msgstr "entra.microsoft.com → Identität → Anwendungen → App-Registrierungen → Neue Registrierung öffnen." -#: includes/class-m365-login-admin.php:1161 +#: includes/class-m365-login-admin.php:1171 msgid "Name: e.g. \"WordPress login\". Supported account types: \"Accounts in this organizational directory only\" (single tenant)." msgstr "Name: z. B. „WordPress Login“. Unterstützte Kontotypen: „Nur Konten in diesem Organisationsverzeichnis“ (Single Tenant)." -#: includes/class-m365-login-admin.php:1162 +#: includes/class-m365-login-admin.php:1172 msgid "Redirect URI: choose the platform Web and paste the URI shown above. Then click Register." msgstr "Umleitungs-URI: Plattform „Web“ wählen und die oben angezeigte URI einfügen. Dann auf „Registrieren“ klicken." -#: includes/class-m365-login-admin.php:1163 +#: includes/class-m365-login-admin.php:1173 msgid "On the Overview page copy the Application (client) ID and the Directory (tenant) ID into the Connection tab." msgstr "Auf der Übersichtsseite die Anwendungs-ID (Client) und die Verzeichnis-ID (Mandant) in den Tab „Verbindung“ kopieren." -#: includes/class-m365-login-admin.php:1164 +#: includes/class-m365-login-admin.php:1174 msgid "Authentication: leave \"ID tokens\" unchecked (the plugin uses the authorization code flow) and \"Allow public client flows\" on No." msgstr "Authentifizierung: „ID-Token“ nicht anhaken (das Plugin nutzt den Authorization Code Flow) und „Öffentliche Clientflows zulassen“ auf „Nein“ lassen." -#: includes/class-m365-login-admin.php:1165 +#: includes/class-m365-login-admin.php:1175 msgid "Token configuration → Add optional claim → ID → tick \"email\" → Add. Confirm the API permission prompt." msgstr "Tokenkonfiguration → Optionalen Anspruch hinzufügen → ID → „email“ anhaken → Hinzufügen. Die Rückfrage zur API-Berechtigung bestätigen." -#: includes/class-m365-login-admin.php:1166 +#: includes/class-m365-login-admin.php:1176 msgid "Pick the authentication method on the Connection tab and follow its step-by-step guide (client secret or certificate)." msgstr "Im Tab „Verbindung“ die Authentifizierungsmethode wählen und der zugehörigen Schritt-für-Schritt-Anleitung folgen (Client Secret oder Zertifikat)." -#: includes/class-m365-login-admin.php:1167 +#: includes/class-m365-login-admin.php:1177 msgid "Optional: restrict who may use the app under Enterprise applications → your app → Properties → \"Assignment required\" = Yes, then assign users/groups." msgstr "Optional: Unter Unternehmensanwendungen → deine App → Eigenschaften → „Zuweisung erforderlich“ = Ja einschränken, wer die App nutzen darf, und dann Benutzer/Gruppen zuweisen." -#: includes/class-m365-login-admin.php:1169 +#: includes/class-m365-login-admin.php:1179 msgid "Required API permission: openid, profile, email (delegated) – granted by default." msgstr "Benötigte API-Berechtigungen: openid, profile, email (delegiert) – standardmäßig vorhanden." -#: includes/class-m365-login-admin.php:1170 +#: includes/class-m365-login-admin.php:1180 msgid "Optional, for group restrictions and the user sync: application permissions GroupMember.Read.All and User.Read.All (Microsoft Graph) with admin consent." msgstr "Optional für Gruppen-Beschränkungen und den Benutzer-Sync: Anwendungsberechtigungen GroupMember.Read.All und User.Read.All (Microsoft Graph) mit Administratorzustimmung." -#: includes/class-m365-login-admin.php:1174 +#: includes/class-m365-login-admin.php:1184 msgid "Shortcode" msgstr "Shortcode" -#: includes/class-m365-login-admin.php:1175 +#: includes/class-m365-login-admin.php:1185 msgid "Place the button on a custom login page:" msgstr "Button auf einer eigenen Login-Seite platzieren:" -#: includes/class-m365-login-admin.php:1177 +#: includes/class-m365-login-admin.php:1187 msgid "More options on the Button tab under \"Custom login page\"." msgstr "Weitere Optionen im Tab „Button“ unter „Eigene Login-Seite“." @@ -1051,75 +1067,79 @@ msgstr "Weitere Optionen im Tab „Button“ unter „Eigene Login-Seite“." msgid "Password sign-in is disabled on this site. Please use the Microsoft button." msgstr "Die Anmeldung mit Passwort ist auf dieser Website deaktiviert. Bitte den Microsoft-Button verwenden." -#: includes/class-m365-login-auth.php:827 +#: includes/class-m365-login-auth.php:906 msgid "Password sign-in is temporarily enabled for this browser (30 minutes)." msgstr "Die Passwort-Anmeldung ist für diesen Browser vorübergehend aktiviert (30 Minuten)." -#: includes/class-m365-login-auth.php:848 includes/class-m365-login-graph.php:63 +#: includes/class-m365-login-auth.php:927 includes/class-m365-login-graph.php:63 msgid "Microsoft login is not configured yet." msgstr "Die Microsoft-Anmeldung ist noch nicht eingerichtet." -#: includes/class-m365-login-auth.php:849 +#: includes/class-m365-login-auth.php:928 msgid "The login request expired or was invalid. Please try again." msgstr "Die Anmeldeanfrage ist abgelaufen oder ungültig. Bitte erneut versuchen." -#: includes/class-m365-login-auth.php:850 +#: includes/class-m365-login-auth.php:929 msgid "Microsoft sign-in was cancelled." msgstr "Die Microsoft-Anmeldung wurde abgebrochen." -#: includes/class-m365-login-auth.php:851 +#: includes/class-m365-login-auth.php:930 msgid "Microsoft returned an error. Please try again." msgstr "Microsoft hat einen Fehler gemeldet. Bitte erneut versuchen." -#: includes/class-m365-login-auth.php:852 +#: includes/class-m365-login-auth.php:931 msgid "Could not complete the sign-in with Microsoft. Please try again or contact an administrator." msgstr "Die Anmeldung über Microsoft konnte nicht abgeschlossen werden. Bitte erneut versuchen oder einen Administrator kontaktieren." -#: includes/class-m365-login-auth.php:853 +#: includes/class-m365-login-auth.php:932 msgid "The Microsoft sign-in could not be verified." msgstr "Die Microsoft-Anmeldung konnte nicht verifiziert werden." -#: includes/class-m365-login-auth.php:854 +#: includes/class-m365-login-auth.php:933 msgid "Your Microsoft account did not provide an e-mail address." msgstr "Das Microsoft-Konto hat keine E-Mail-Adresse übermittelt." -#: includes/class-m365-login-auth.php:855 +#: includes/class-m365-login-auth.php:934 msgid "Your e-mail domain is not allowed to sign in here." msgstr "Diese E-Mail-Domain ist hier nicht zur Anmeldung zugelassen." -#: includes/class-m365-login-auth.php:856 +#: includes/class-m365-login-auth.php:935 msgid "No WordPress account exists for your Microsoft e-mail address." msgstr "Für die E-Mail-Adresse des Microsoft-Kontos existiert kein WordPress-Konto." -#: includes/class-m365-login-auth.php:857 +#: includes/class-m365-login-auth.php:936 msgid "This WordPress account is linked to a different Microsoft account. Please contact an administrator." msgstr "Dieses WordPress-Konto ist mit einem anderen Microsoft-Konto verknüpft. Bitte einen Administrator kontaktieren." -#: includes/class-m365-login-auth.php:858 +#: includes/class-m365-login-auth.php:937 msgid "You are not allowed to sign in with this account." msgstr "Die Anmeldung mit diesem Konto ist nicht erlaubt." -#: includes/class-m365-login-auth.php:859 +#: includes/class-m365-login-auth.php:938 msgid "Your Microsoft account is not a member of a group that is allowed to sign in here." msgstr "Das Microsoft-Konto ist in keiner Gruppe, die sich hier anmelden darf." -#: includes/class-m365-login-auth.php:860 +#: includes/class-m365-login-auth.php:939 msgid "Your group membership could not be verified. Please contact an administrator." msgstr "Die Gruppenmitgliedschaft konnte nicht geprüft werden. Bitte einen Administrator kontaktieren." -#: includes/class-m365-login-auth.php:861 +#: includes/class-m365-login-auth.php:940 +msgid "Your Microsoft account is a member of a group that is not allowed to sign in here." +msgstr "Dein Microsoft-Konto ist Mitglied einer Gruppe, die sich hier nicht anmelden darf." + +#: includes/class-m365-login-auth.php:941 msgid "The fallback key is not valid." msgstr "Der Fallback-Schlüssel ist ungültig." -#: includes/class-m365-login-auth.php:862 +#: includes/class-m365-login-auth.php:942 msgid "Too many attempts. Please wait 15 minutes." msgstr "Zu viele Versuche. Bitte 15 Minuten warten." -#: includes/class-m365-login-auth.php:863 +#: includes/class-m365-login-auth.php:943 msgid "Too many sign-in attempts from your connection. Please wait a few minutes and try again." msgstr "Zu viele Anmeldeversuche von dieser Verbindung. Bitte ein paar Minuten warten und erneut versuchen." -#: includes/class-m365-login-auth.php:864 includes/class-m365-login-sync.php:1520 +#: includes/class-m365-login-auth.php:944 includes/class-m365-login-sync.php:1520 msgid "This account has been deactivated." msgstr "Dieses Konto wurde deaktiviert." @@ -1187,51 +1207,51 @@ msgstr "Sicherheitsgruppe" msgid "Microsoft 365 group" msgstr "Microsoft 365-Gruppe" -#: includes/class-m365-login-settings.php:54 +#: includes/class-m365-login-settings.php:55 msgid "Sign in with Microsoft" msgstr "Login mit Microsoft" -#: includes/class-m365-login-settings.php:63 +#: includes/class-m365-login-settings.php:64 msgid "or" msgstr "oder" -#: includes/class-m365-login-settings.php:225 includes/class-m365-login-settings.php:530 +#: includes/class-m365-login-settings.php:226 includes/class-m365-login-settings.php:540 msgid "The private key could not be encrypted. Is the OpenSSL extension available?" msgstr "Der private Schlüssel konnte nicht verschlüsselt werden. Ist die OpenSSL-Erweiterung verfügbar?" -#: includes/class-m365-login-settings.php:476 +#: includes/class-m365-login-settings.php:486 msgid "The tenant ID must be a GUID (e.g. 1a2b3c4d-…) or one of \"organizations\", \"common\", \"consumers\"." msgstr "Die Tenant-ID muss eine GUID (z. B. 1a2b3c4d-…) oder einer der Werte „organizations“, „common“, „consumers“ sein." -#: includes/class-m365-login-settings.php:484 +#: includes/class-m365-login-settings.php:494 msgid "The application (client) ID must be a GUID." msgstr "Die Anwendungs-ID (Client) muss eine GUID sein." -#: includes/class-m365-login-settings.php:496 +#: includes/class-m365-login-settings.php:506 msgid "The client secret contains invalid characters." msgstr "Das Client Secret enthält ungültige Zeichen." -#: includes/class-m365-login-settings.php:500 +#: includes/class-m365-login-settings.php:510 msgid "The client secret could not be encrypted. Is the OpenSSL extension available?" msgstr "Das Client Secret konnte nicht verschlüsselt werden. Ist die OpenSSL-Erweiterung verfügbar?" -#: includes/class-m365-login-settings.php:520 +#: includes/class-m365-login-settings.php:530 msgid "Please paste both the private key and the certificate." msgstr "Bitte sowohl den privaten Schlüssel als auch das Zertifikat einfügen." -#: includes/class-m365-login-settings.php:522 +#: includes/class-m365-login-settings.php:532 msgid "The pasted key or certificate is too large." msgstr "Der eingefügte Schlüssel oder das Zertifikat ist zu groß." -#: includes/class-m365-login-settings.php:539 +#: includes/class-m365-login-settings.php:549 msgid "Certificate authentication is selected but no certificate is stored yet. Generate one or paste your own; the Microsoft button stays hidden until then." msgstr "Zertifikats-Authentifizierung ist ausgewählt, aber es ist noch kein Zertifikat gespeichert. Eines erzeugen oder ein eigenes einfügen; bis dahin bleibt der Microsoft-Button ausgeblendet." -#: includes/class-m365-login-settings.php:571 +#: includes/class-m365-login-settings.php:582 msgid "The custom login page must be a URL on this site." msgstr "Die eigene Login-Seite muss eine URL dieser Website sein." -#: includes/class-m365-login-settings.php:678 +#: includes/class-m365-login-settings.php:689 msgid "User sync: \"Delete\" needs a user who receives the posts of deleted accounts. Until one is selected, accounts are deactivated instead." msgstr "Benutzer-Sync: „Löschen“ braucht einen Benutzer, der die Beiträge gelöschter Konten übernimmt. Bis einer ausgewählt ist, werden Konten stattdessen deaktiviert." diff --git a/languages/m365-login-de_DE_formal.mo b/languages/m365-login-de_DE_formal.mo index d7269bd..dcc1421 100644 Binary files a/languages/m365-login-de_DE_formal.mo and b/languages/m365-login-de_DE_formal.mo differ diff --git a/languages/m365-login-de_DE_formal.po b/languages/m365-login-de_DE_formal.po index 2a828fa..7ec2925 100644 --- a/languages/m365-login-de_DE_formal.po +++ b/languages/m365-login-de_DE_formal.po @@ -56,7 +56,7 @@ msgstr "Dieses Icon verwenden" msgid "Copied!" msgstr "Kopiert!" -#: includes/class-m365-login-admin.php:233 includes/class-m365-login-admin.php:848 includes/class-m365-login-admin.php:1102 includes/class-m365-login-admin.php:1147 +#: includes/class-m365-login-admin.php:233 includes/class-m365-login-admin.php:848 includes/class-m365-login-admin.php:1112 includes/class-m365-login-admin.php:1157 msgid "Copy" msgstr "Kopieren" @@ -914,136 +914,152 @@ msgid "Membership is read from the \"groups\" claim of the ID token when present msgstr "Die Mitgliedschaft wird aus dem „groups“-Claim des ID-Tokens gelesen, falls vorhanden; andernfalls fragt das Plugin Microsoft Graph (Anwendungsberechtigung „User.Read.All“ oder „Directory.Read.All“). Funktioniert beides nicht, wird die Anmeldung abgelehnt." #: includes/class-m365-login-admin.php:1085 +msgid "Excluded Entra groups (optional)" +msgstr "Ausgeschlossene Entra-Gruppen (optional)" + +#: includes/class-m365-login-admin.php:1086 +msgid "Members of these groups can never sign in with Microsoft – even if they are in an allowed group. Nested memberships count." +msgstr "Mitglieder dieser Gruppen können sich nie per Microsoft anmelden – auch nicht, wenn sie in einer erlaubten Gruppe sind. Verschachtelte Mitgliedschaften zählen." + +#: includes/class-m365-login-admin.php:1088 +msgid "No groups excluded." +msgstr "Keine Gruppen ausgeschlossen." + +#: includes/class-m365-login-admin.php:1090 +msgid "The plugin asks Microsoft Graph on every sign-in (application permission \"User.Read.All\" or \"Directory.Read.All\"), because a \"groups\" claim may be filtered and cannot prove that someone is not a member. If the check fails, the sign-in is refused. Password sign-in is not affected – combine with button-only mode if needed." +msgstr "Das Plugin fragt bei jeder Anmeldung Microsoft Graph (Anwendungsberechtigung „User.Read.All“ oder „Directory.Read.All“), weil ein „groups“-Claim gefiltert sein kann und nicht beweist, dass jemand kein Mitglied ist. Schlägt die Prüfung fehl, wird die Anmeldung abgelehnt. Die Passwort-Anmeldung ist nicht betroffen – bei Bedarf mit dem Nur-Button-Modus kombinieren." + +#: includes/class-m365-login-admin.php:1095 msgid "Button-only mode" msgstr "Nur-Button-Modus" -#: includes/class-m365-login-admin.php:1086 +#: includes/class-m365-login-admin.php:1096 msgid "Hides the username/password fields (on wp-login.php and in wp_login_form() forms) and refuses every interactive password sign-in on the site, including custom login forms. Application passwords, REST, XML-RPC and WP-CLI are not affected." msgstr "Blendet die Benutzername/Passwort-Felder aus (auf wp-login.php und in wp_login_form()-Formularen) und lehnt jede interaktive Passwort-Anmeldung auf der Website ab, auch in eigenen Login-Formularen. Anwendungspasswörter, REST, XML-RPC und WP-CLI sind nicht betroffen." -#: includes/class-m365-login-admin.php:1091 +#: includes/class-m365-login-admin.php:1101 msgid "Show only the Microsoft button on the login page" msgstr "Auf der Login-Seite nur den Microsoft-Button anzeigen" -#: includes/class-m365-login-admin.php:1092 +#: includes/class-m365-login-admin.php:1102 msgid "Becomes active once the connection is configured. Make sure your own account can sign in via Microsoft before enabling this." msgstr "Wird aktiv, sobald die Verbindung eingerichtet ist. Vor dem Aktivieren sicherstellen, dass das eigene Konto sich per Microsoft anmelden kann." -#: includes/class-m365-login-admin.php:1097 +#: includes/class-m365-login-admin.php:1107 msgid "Fallback link (keep it secret)" msgstr "Fallback-Link (geheim halten)" -#: includes/class-m365-login-admin.php:1098 +#: includes/class-m365-login-admin.php:1108 msgid "Opening this link shows the password form again in that browser for 30 minutes and allows password sign-in there. Bookmark it somewhere safe – it is your way back in if Microsoft sign-in ever breaks." msgstr "Wer diesen Link öffnet, sieht in diesem Browser 30 Minuten lang wieder das Passwort-Formular und kann sich dort mit Passwort anmelden. Sicher aufbewahren – er ist der Weg zurück, falls die Microsoft-Anmeldung einmal nicht funktioniert." -#: includes/class-m365-login-admin.php:1106 +#: includes/class-m365-login-admin.php:1116 msgid "Generate a new key when saving" msgstr "Beim Speichern einen neuen Schlüssel erzeugen" -#: includes/class-m365-login-admin.php:1109 +#: includes/class-m365-login-admin.php:1119 msgid "A key is generated automatically the first time you save these settings." msgstr "Beim ersten Speichern dieser Einstellungen wird automatisch ein Schlüssel erzeugt." #. translators: %s: PHP constant -#: includes/class-m365-login-admin.php:1115 +#: includes/class-m365-login-admin.php:1125 msgid "Emergency switch: add %s to wp-config.php to disable button-only mode entirely." msgstr "Notschalter: %s in die wp-config.php eintragen, um den Nur-Button-Modus vollständig abzuschalten." #. translators: %s: PHP constant -#: includes/class-m365-login-admin.php:1124 +#: includes/class-m365-login-admin.php:1134 msgid "What the plugin does to keep sign-ins safe" msgstr "So schützt das Plugin die Anmeldung" -#: includes/class-m365-login-admin.php:1126 +#: includes/class-m365-login-admin.php:1136 msgid "OpenID Connect authorization code flow with PKCE (S256) – no tokens ever pass through the browser." msgstr "OpenID Connect Authorization Code Flow mit PKCE (S256) – Tokens laufen nie durch den Browser." -#: includes/class-m365-login-admin.php:1127 +#: includes/class-m365-login-admin.php:1137 msgid "Single-use state and nonce values bound to the browser via an HttpOnly cookie (CSRF and replay protection)." msgstr "Einmalige State- und Nonce-Werte, per HttpOnly-Cookie an den Browser gebunden (CSRF- und Replay-Schutz)." -#: includes/class-m365-login-admin.php:1128 +#: includes/class-m365-login-admin.php:1138 msgid "ID token signature verified against Microsoft’s published signing keys; issuer, audience, tenant, expiry and nonce are checked." msgstr "Signatur des ID-Tokens wird gegen Microsofts veröffentlichte Signaturschlüssel geprüft; Issuer, Audience, Tenant, Ablauf und Nonce werden kontrolliert." -#: includes/class-m365-login-admin.php:1129 +#: includes/class-m365-login-admin.php:1139 msgid "Client secret encrypted at rest; sign-in never creates accounts or changes passwords." msgstr "Client Secret verschlüsselt gespeichert; die Anmeldung legt nie Konten an und ändert keine Passwörter." -#: includes/class-m365-login-admin.php:1137 +#: includes/class-m365-login-admin.php:1147 msgid "Save changes" msgstr "Änderungen speichern" -#: includes/class-m365-login-admin.php:1143 +#: includes/class-m365-login-admin.php:1153 msgid "Redirect URI" msgstr "Umleitungs-URI (Redirect URI)" -#: includes/class-m365-login-admin.php:1144 +#: includes/class-m365-login-admin.php:1154 msgid "Register this URI in your app registration under Authentication → Web → Redirect URIs:" msgstr "Diese URI in der App-Registrierung unter Authentifizierung → Web → Umleitungs-URIs eintragen:" -#: includes/class-m365-login-admin.php:1150 +#: includes/class-m365-login-admin.php:1160 msgid "Plain permalinks are active, so the callback uses a query string. If you enable pretty permalinks later, the redirect URI changes and must be updated in Entra ID." msgstr "Einfache Permalinks sind aktiv, daher verwendet der Callback einen Query-String. Werden später sprechende Permalinks aktiviert, ändert sich die Umleitungs-URI und muss in Entra ID angepasst werden." -#: includes/class-m365-login-admin.php:1153 +#: includes/class-m365-login-admin.php:1163 msgid "Your site does not use HTTPS. Microsoft only accepts http:// redirect URIs for localhost; production sites must use HTTPS." msgstr "Diese Website nutzt kein HTTPS. Microsoft akzeptiert http://-Umleitungs-URIs nur für localhost; produktive Websites benötigen HTTPS." -#: includes/class-m365-login-admin.php:1158 +#: includes/class-m365-login-admin.php:1168 msgid "Setup guide: app registration" msgstr "Anleitung: App-Registrierung" -#: includes/class-m365-login-admin.php:1160 +#: includes/class-m365-login-admin.php:1170 msgid "Open entra.microsoft.com → Identity → Applications → App registrations → New registration." msgstr "entra.microsoft.com → Identität → Anwendungen → App-Registrierungen → Neue Registrierung öffnen." -#: includes/class-m365-login-admin.php:1161 +#: includes/class-m365-login-admin.php:1171 msgid "Name: e.g. \"WordPress login\". Supported account types: \"Accounts in this organizational directory only\" (single tenant)." msgstr "Name: z. B. „WordPress Login“. Unterstützte Kontotypen: „Nur Konten in diesem Organisationsverzeichnis“ (Single Tenant)." -#: includes/class-m365-login-admin.php:1162 +#: includes/class-m365-login-admin.php:1172 msgid "Redirect URI: choose the platform Web and paste the URI shown above. Then click Register." msgstr "Umleitungs-URI: Plattform „Web“ wählen und die oben angezeigte URI einfügen. Dann auf „Registrieren“ klicken." -#: includes/class-m365-login-admin.php:1163 +#: includes/class-m365-login-admin.php:1173 msgid "On the Overview page copy the Application (client) ID and the Directory (tenant) ID into the Connection tab." msgstr "Auf der Übersichtsseite die Anwendungs-ID (Client) und die Verzeichnis-ID (Mandant) in den Tab „Verbindung“ kopieren." -#: includes/class-m365-login-admin.php:1164 +#: includes/class-m365-login-admin.php:1174 msgid "Authentication: leave \"ID tokens\" unchecked (the plugin uses the authorization code flow) and \"Allow public client flows\" on No." msgstr "Authentifizierung: „ID-Token“ nicht anhaken (das Plugin nutzt den Authorization Code Flow) und „Öffentliche Clientflows zulassen“ auf „Nein“ lassen." -#: includes/class-m365-login-admin.php:1165 +#: includes/class-m365-login-admin.php:1175 msgid "Token configuration → Add optional claim → ID → tick \"email\" → Add. Confirm the API permission prompt." msgstr "Tokenkonfiguration → Optionalen Anspruch hinzufügen → ID → „email“ anhaken → Hinzufügen. Die Rückfrage zur API-Berechtigung bestätigen." -#: includes/class-m365-login-admin.php:1166 +#: includes/class-m365-login-admin.php:1176 msgid "Pick the authentication method on the Connection tab and follow its step-by-step guide (client secret or certificate)." msgstr "Im Tab „Verbindung“ die Authentifizierungsmethode wählen und der zugehörigen Schritt-für-Schritt-Anleitung folgen (Client Secret oder Zertifikat)." -#: includes/class-m365-login-admin.php:1167 +#: includes/class-m365-login-admin.php:1177 msgid "Optional: restrict who may use the app under Enterprise applications → your app → Properties → \"Assignment required\" = Yes, then assign users/groups." msgstr "Optional: Unter Unternehmensanwendungen → Ihre App → Eigenschaften → „Zuweisung erforderlich“ = Ja einschränken, wer die App nutzen darf, und dann Benutzer/Gruppen zuweisen." -#: includes/class-m365-login-admin.php:1169 +#: includes/class-m365-login-admin.php:1179 msgid "Required API permission: openid, profile, email (delegated) – granted by default." msgstr "Benötigte API-Berechtigungen: openid, profile, email (delegiert) – standardmäßig vorhanden." -#: includes/class-m365-login-admin.php:1170 +#: includes/class-m365-login-admin.php:1180 msgid "Optional, for group restrictions and the user sync: application permissions GroupMember.Read.All and User.Read.All (Microsoft Graph) with admin consent." msgstr "Optional für Gruppen-Beschränkungen und den Benutzer-Sync: Anwendungsberechtigungen GroupMember.Read.All und User.Read.All (Microsoft Graph) mit Administratorzustimmung." -#: includes/class-m365-login-admin.php:1174 +#: includes/class-m365-login-admin.php:1184 msgid "Shortcode" msgstr "Shortcode" -#: includes/class-m365-login-admin.php:1175 +#: includes/class-m365-login-admin.php:1185 msgid "Place the button on a custom login page:" msgstr "Button auf einer eigenen Login-Seite platzieren:" -#: includes/class-m365-login-admin.php:1177 +#: includes/class-m365-login-admin.php:1187 msgid "More options on the Button tab under \"Custom login page\"." msgstr "Weitere Optionen im Tab „Button“ unter „Eigene Login-Seite“." @@ -1051,75 +1067,79 @@ msgstr "Weitere Optionen im Tab „Button“ unter „Eigene Login-Seite“." msgid "Password sign-in is disabled on this site. Please use the Microsoft button." msgstr "Die Anmeldung mit Passwort ist auf dieser Website deaktiviert. Bitte den Microsoft-Button verwenden." -#: includes/class-m365-login-auth.php:827 +#: includes/class-m365-login-auth.php:906 msgid "Password sign-in is temporarily enabled for this browser (30 minutes)." msgstr "Die Passwort-Anmeldung ist für diesen Browser vorübergehend aktiviert (30 Minuten)." -#: includes/class-m365-login-auth.php:848 includes/class-m365-login-graph.php:63 +#: includes/class-m365-login-auth.php:927 includes/class-m365-login-graph.php:63 msgid "Microsoft login is not configured yet." msgstr "Die Microsoft-Anmeldung ist noch nicht eingerichtet." -#: includes/class-m365-login-auth.php:849 +#: includes/class-m365-login-auth.php:928 msgid "The login request expired or was invalid. Please try again." msgstr "Die Anmeldeanfrage ist abgelaufen oder ungültig. Bitte erneut versuchen." -#: includes/class-m365-login-auth.php:850 +#: includes/class-m365-login-auth.php:929 msgid "Microsoft sign-in was cancelled." msgstr "Die Microsoft-Anmeldung wurde abgebrochen." -#: includes/class-m365-login-auth.php:851 +#: includes/class-m365-login-auth.php:930 msgid "Microsoft returned an error. Please try again." msgstr "Microsoft hat einen Fehler gemeldet. Bitte erneut versuchen." -#: includes/class-m365-login-auth.php:852 +#: includes/class-m365-login-auth.php:931 msgid "Could not complete the sign-in with Microsoft. Please try again or contact an administrator." msgstr "Die Anmeldung über Microsoft konnte nicht abgeschlossen werden. Bitte erneut versuchen oder einen Administrator kontaktieren." -#: includes/class-m365-login-auth.php:853 +#: includes/class-m365-login-auth.php:932 msgid "The Microsoft sign-in could not be verified." msgstr "Die Microsoft-Anmeldung konnte nicht verifiziert werden." -#: includes/class-m365-login-auth.php:854 +#: includes/class-m365-login-auth.php:933 msgid "Your Microsoft account did not provide an e-mail address." msgstr "Das Microsoft-Konto hat keine E-Mail-Adresse übermittelt." -#: includes/class-m365-login-auth.php:855 +#: includes/class-m365-login-auth.php:934 msgid "Your e-mail domain is not allowed to sign in here." msgstr "Diese E-Mail-Domain ist hier nicht zur Anmeldung zugelassen." -#: includes/class-m365-login-auth.php:856 +#: includes/class-m365-login-auth.php:935 msgid "No WordPress account exists for your Microsoft e-mail address." msgstr "Für die E-Mail-Adresse des Microsoft-Kontos existiert kein WordPress-Konto." -#: includes/class-m365-login-auth.php:857 +#: includes/class-m365-login-auth.php:936 msgid "This WordPress account is linked to a different Microsoft account. Please contact an administrator." msgstr "Dieses WordPress-Konto ist mit einem anderen Microsoft-Konto verknüpft. Bitte einen Administrator kontaktieren." -#: includes/class-m365-login-auth.php:858 +#: includes/class-m365-login-auth.php:937 msgid "You are not allowed to sign in with this account." msgstr "Die Anmeldung mit diesem Konto ist nicht erlaubt." -#: includes/class-m365-login-auth.php:859 +#: includes/class-m365-login-auth.php:938 msgid "Your Microsoft account is not a member of a group that is allowed to sign in here." msgstr "Das Microsoft-Konto ist in keiner Gruppe, die sich hier anmelden darf." -#: includes/class-m365-login-auth.php:860 +#: includes/class-m365-login-auth.php:939 msgid "Your group membership could not be verified. Please contact an administrator." msgstr "Die Gruppenmitgliedschaft konnte nicht geprüft werden. Bitte einen Administrator kontaktieren." -#: includes/class-m365-login-auth.php:861 +#: includes/class-m365-login-auth.php:940 +msgid "Your Microsoft account is a member of a group that is not allowed to sign in here." +msgstr "Ihr Microsoft-Konto ist Mitglied einer Gruppe, die sich hier nicht anmelden darf." + +#: includes/class-m365-login-auth.php:941 msgid "The fallback key is not valid." msgstr "Der Fallback-Schlüssel ist ungültig." -#: includes/class-m365-login-auth.php:862 +#: includes/class-m365-login-auth.php:942 msgid "Too many attempts. Please wait 15 minutes." msgstr "Zu viele Versuche. Bitte 15 Minuten warten." -#: includes/class-m365-login-auth.php:863 +#: includes/class-m365-login-auth.php:943 msgid "Too many sign-in attempts from your connection. Please wait a few minutes and try again." msgstr "Zu viele Anmeldeversuche von dieser Verbindung. Bitte ein paar Minuten warten und erneut versuchen." -#: includes/class-m365-login-auth.php:864 includes/class-m365-login-sync.php:1520 +#: includes/class-m365-login-auth.php:944 includes/class-m365-login-sync.php:1520 msgid "This account has been deactivated." msgstr "Dieses Konto wurde deaktiviert." @@ -1187,51 +1207,51 @@ msgstr "Sicherheitsgruppe" msgid "Microsoft 365 group" msgstr "Microsoft 365-Gruppe" -#: includes/class-m365-login-settings.php:54 +#: includes/class-m365-login-settings.php:55 msgid "Sign in with Microsoft" msgstr "Login mit Microsoft" -#: includes/class-m365-login-settings.php:63 +#: includes/class-m365-login-settings.php:64 msgid "or" msgstr "oder" -#: includes/class-m365-login-settings.php:225 includes/class-m365-login-settings.php:530 +#: includes/class-m365-login-settings.php:226 includes/class-m365-login-settings.php:540 msgid "The private key could not be encrypted. Is the OpenSSL extension available?" msgstr "Der private Schlüssel konnte nicht verschlüsselt werden. Ist die OpenSSL-Erweiterung verfügbar?" -#: includes/class-m365-login-settings.php:476 +#: includes/class-m365-login-settings.php:486 msgid "The tenant ID must be a GUID (e.g. 1a2b3c4d-…) or one of \"organizations\", \"common\", \"consumers\"." msgstr "Die Tenant-ID muss eine GUID (z. B. 1a2b3c4d-…) oder einer der Werte „organizations“, „common“, „consumers“ sein." -#: includes/class-m365-login-settings.php:484 +#: includes/class-m365-login-settings.php:494 msgid "The application (client) ID must be a GUID." msgstr "Die Anwendungs-ID (Client) muss eine GUID sein." -#: includes/class-m365-login-settings.php:496 +#: includes/class-m365-login-settings.php:506 msgid "The client secret contains invalid characters." msgstr "Das Client Secret enthält ungültige Zeichen." -#: includes/class-m365-login-settings.php:500 +#: includes/class-m365-login-settings.php:510 msgid "The client secret could not be encrypted. Is the OpenSSL extension available?" msgstr "Das Client Secret konnte nicht verschlüsselt werden. Ist die OpenSSL-Erweiterung verfügbar?" -#: includes/class-m365-login-settings.php:520 +#: includes/class-m365-login-settings.php:530 msgid "Please paste both the private key and the certificate." msgstr "Bitte sowohl den privaten Schlüssel als auch das Zertifikat einfügen." -#: includes/class-m365-login-settings.php:522 +#: includes/class-m365-login-settings.php:532 msgid "The pasted key or certificate is too large." msgstr "Der eingefügte Schlüssel oder das Zertifikat ist zu groß." -#: includes/class-m365-login-settings.php:539 +#: includes/class-m365-login-settings.php:549 msgid "Certificate authentication is selected but no certificate is stored yet. Generate one or paste your own; the Microsoft button stays hidden until then." msgstr "Zertifikats-Authentifizierung ist ausgewählt, aber es ist noch kein Zertifikat gespeichert. Eines erzeugen oder ein eigenes einfügen; bis dahin bleibt der Microsoft-Button ausgeblendet." -#: includes/class-m365-login-settings.php:571 +#: includes/class-m365-login-settings.php:582 msgid "The custom login page must be a URL on this site." msgstr "Die eigene Login-Seite muss eine URL dieser Website sein." -#: includes/class-m365-login-settings.php:678 +#: includes/class-m365-login-settings.php:689 msgid "User sync: \"Delete\" needs a user who receives the posts of deleted accounts. Until one is selected, accounts are deactivated instead." msgstr "Benutzer-Sync: „Löschen“ braucht einen Benutzer, der die Beiträge gelöschter Konten übernimmt. Bis einer ausgewählt ist, werden Konten stattdessen deaktiviert." diff --git a/languages/m365-login.pot b/languages/m365-login.pot index 299d0c8..2af22c3 100644 --- a/languages/m365-login.pot +++ b/languages/m365-login.pot @@ -54,7 +54,7 @@ msgstr "" msgid "Copied!" msgstr "" -#: includes/class-m365-login-admin.php:233 includes/class-m365-login-admin.php:848 includes/class-m365-login-admin.php:1102 includes/class-m365-login-admin.php:1147 +#: includes/class-m365-login-admin.php:233 includes/class-m365-login-admin.php:848 includes/class-m365-login-admin.php:1112 includes/class-m365-login-admin.php:1157 msgid "Copy" msgstr "" @@ -912,136 +912,152 @@ msgid "Membership is read from the \"groups\" claim of the ID token when present msgstr "" #: includes/class-m365-login-admin.php:1085 -msgid "Button-only mode" +msgid "Excluded Entra groups (optional)" msgstr "" #: includes/class-m365-login-admin.php:1086 +msgid "Members of these groups can never sign in with Microsoft – even if they are in an allowed group. Nested memberships count." +msgstr "" + +#: includes/class-m365-login-admin.php:1088 +msgid "No groups excluded." +msgstr "" + +#: includes/class-m365-login-admin.php:1090 +msgid "The plugin asks Microsoft Graph on every sign-in (application permission \"User.Read.All\" or \"Directory.Read.All\"), because a \"groups\" claim may be filtered and cannot prove that someone is not a member. If the check fails, the sign-in is refused. Password sign-in is not affected – combine with button-only mode if needed." +msgstr "" + +#: includes/class-m365-login-admin.php:1095 +msgid "Button-only mode" +msgstr "" + +#: includes/class-m365-login-admin.php:1096 msgid "Hides the username/password fields (on wp-login.php and in wp_login_form() forms) and refuses every interactive password sign-in on the site, including custom login forms. Application passwords, REST, XML-RPC and WP-CLI are not affected." msgstr "" -#: includes/class-m365-login-admin.php:1091 +#: includes/class-m365-login-admin.php:1101 msgid "Show only the Microsoft button on the login page" msgstr "" -#: includes/class-m365-login-admin.php:1092 +#: includes/class-m365-login-admin.php:1102 msgid "Becomes active once the connection is configured. Make sure your own account can sign in via Microsoft before enabling this." msgstr "" -#: includes/class-m365-login-admin.php:1097 +#: includes/class-m365-login-admin.php:1107 msgid "Fallback link (keep it secret)" msgstr "" -#: includes/class-m365-login-admin.php:1098 +#: includes/class-m365-login-admin.php:1108 msgid "Opening this link shows the password form again in that browser for 30 minutes and allows password sign-in there. Bookmark it somewhere safe – it is your way back in if Microsoft sign-in ever breaks." msgstr "" -#: includes/class-m365-login-admin.php:1106 +#: includes/class-m365-login-admin.php:1116 msgid "Generate a new key when saving" msgstr "" -#: includes/class-m365-login-admin.php:1109 +#: includes/class-m365-login-admin.php:1119 msgid "A key is generated automatically the first time you save these settings." msgstr "" #. translators: %s: PHP constant -#: includes/class-m365-login-admin.php:1115 +#: includes/class-m365-login-admin.php:1125 msgid "Emergency switch: add %s to wp-config.php to disable button-only mode entirely." msgstr "" #. translators: %s: PHP constant -#: includes/class-m365-login-admin.php:1124 +#: includes/class-m365-login-admin.php:1134 msgid "What the plugin does to keep sign-ins safe" msgstr "" -#: includes/class-m365-login-admin.php:1126 +#: includes/class-m365-login-admin.php:1136 msgid "OpenID Connect authorization code flow with PKCE (S256) – no tokens ever pass through the browser." msgstr "" -#: includes/class-m365-login-admin.php:1127 +#: includes/class-m365-login-admin.php:1137 msgid "Single-use state and nonce values bound to the browser via an HttpOnly cookie (CSRF and replay protection)." msgstr "" -#: includes/class-m365-login-admin.php:1128 +#: includes/class-m365-login-admin.php:1138 msgid "ID token signature verified against Microsoft’s published signing keys; issuer, audience, tenant, expiry and nonce are checked." msgstr "" -#: includes/class-m365-login-admin.php:1129 +#: includes/class-m365-login-admin.php:1139 msgid "Client secret encrypted at rest; sign-in never creates accounts or changes passwords." msgstr "" -#: includes/class-m365-login-admin.php:1137 +#: includes/class-m365-login-admin.php:1147 msgid "Save changes" msgstr "" -#: includes/class-m365-login-admin.php:1143 +#: includes/class-m365-login-admin.php:1153 msgid "Redirect URI" msgstr "" -#: includes/class-m365-login-admin.php:1144 +#: includes/class-m365-login-admin.php:1154 msgid "Register this URI in your app registration under Authentication → Web → Redirect URIs:" msgstr "" -#: includes/class-m365-login-admin.php:1150 +#: includes/class-m365-login-admin.php:1160 msgid "Plain permalinks are active, so the callback uses a query string. If you enable pretty permalinks later, the redirect URI changes and must be updated in Entra ID." msgstr "" -#: includes/class-m365-login-admin.php:1153 +#: includes/class-m365-login-admin.php:1163 msgid "Your site does not use HTTPS. Microsoft only accepts http:// redirect URIs for localhost; production sites must use HTTPS." msgstr "" -#: includes/class-m365-login-admin.php:1158 +#: includes/class-m365-login-admin.php:1168 msgid "Setup guide: app registration" msgstr "" -#: includes/class-m365-login-admin.php:1160 +#: includes/class-m365-login-admin.php:1170 msgid "Open entra.microsoft.com → Identity → Applications → App registrations → New registration." msgstr "" -#: includes/class-m365-login-admin.php:1161 +#: includes/class-m365-login-admin.php:1171 msgid "Name: e.g. \"WordPress login\". Supported account types: \"Accounts in this organizational directory only\" (single tenant)." msgstr "" -#: includes/class-m365-login-admin.php:1162 +#: includes/class-m365-login-admin.php:1172 msgid "Redirect URI: choose the platform Web and paste the URI shown above. Then click Register." msgstr "" -#: includes/class-m365-login-admin.php:1163 +#: includes/class-m365-login-admin.php:1173 msgid "On the Overview page copy the Application (client) ID and the Directory (tenant) ID into the Connection tab." msgstr "" -#: includes/class-m365-login-admin.php:1164 +#: includes/class-m365-login-admin.php:1174 msgid "Authentication: leave \"ID tokens\" unchecked (the plugin uses the authorization code flow) and \"Allow public client flows\" on No." msgstr "" -#: includes/class-m365-login-admin.php:1165 +#: includes/class-m365-login-admin.php:1175 msgid "Token configuration → Add optional claim → ID → tick \"email\" → Add. Confirm the API permission prompt." msgstr "" -#: includes/class-m365-login-admin.php:1166 +#: includes/class-m365-login-admin.php:1176 msgid "Pick the authentication method on the Connection tab and follow its step-by-step guide (client secret or certificate)." msgstr "" -#: includes/class-m365-login-admin.php:1167 +#: includes/class-m365-login-admin.php:1177 msgid "Optional: restrict who may use the app under Enterprise applications → your app → Properties → \"Assignment required\" = Yes, then assign users/groups." msgstr "" -#: includes/class-m365-login-admin.php:1169 +#: includes/class-m365-login-admin.php:1179 msgid "Required API permission: openid, profile, email (delegated) – granted by default." msgstr "" -#: includes/class-m365-login-admin.php:1170 +#: includes/class-m365-login-admin.php:1180 msgid "Optional, for group restrictions and the user sync: application permissions GroupMember.Read.All and User.Read.All (Microsoft Graph) with admin consent." msgstr "" -#: includes/class-m365-login-admin.php:1174 +#: includes/class-m365-login-admin.php:1184 msgid "Shortcode" msgstr "" -#: includes/class-m365-login-admin.php:1175 +#: includes/class-m365-login-admin.php:1185 msgid "Place the button on a custom login page:" msgstr "" -#: includes/class-m365-login-admin.php:1177 +#: includes/class-m365-login-admin.php:1187 msgid "More options on the Button tab under \"Custom login page\"." msgstr "" @@ -1049,75 +1065,79 @@ msgstr "" msgid "Password sign-in is disabled on this site. Please use the Microsoft button." msgstr "" -#: includes/class-m365-login-auth.php:827 +#: includes/class-m365-login-auth.php:906 msgid "Password sign-in is temporarily enabled for this browser (30 minutes)." msgstr "" -#: includes/class-m365-login-auth.php:848 includes/class-m365-login-graph.php:63 +#: includes/class-m365-login-auth.php:927 includes/class-m365-login-graph.php:63 msgid "Microsoft login is not configured yet." msgstr "" -#: includes/class-m365-login-auth.php:849 +#: includes/class-m365-login-auth.php:928 msgid "The login request expired or was invalid. Please try again." msgstr "" -#: includes/class-m365-login-auth.php:850 +#: includes/class-m365-login-auth.php:929 msgid "Microsoft sign-in was cancelled." msgstr "" -#: includes/class-m365-login-auth.php:851 +#: includes/class-m365-login-auth.php:930 msgid "Microsoft returned an error. Please try again." msgstr "" -#: includes/class-m365-login-auth.php:852 +#: includes/class-m365-login-auth.php:931 msgid "Could not complete the sign-in with Microsoft. Please try again or contact an administrator." msgstr "" -#: includes/class-m365-login-auth.php:853 +#: includes/class-m365-login-auth.php:932 msgid "The Microsoft sign-in could not be verified." msgstr "" -#: includes/class-m365-login-auth.php:854 +#: includes/class-m365-login-auth.php:933 msgid "Your Microsoft account did not provide an e-mail address." msgstr "" -#: includes/class-m365-login-auth.php:855 +#: includes/class-m365-login-auth.php:934 msgid "Your e-mail domain is not allowed to sign in here." msgstr "" -#: includes/class-m365-login-auth.php:856 +#: includes/class-m365-login-auth.php:935 msgid "No WordPress account exists for your Microsoft e-mail address." msgstr "" -#: includes/class-m365-login-auth.php:857 +#: includes/class-m365-login-auth.php:936 msgid "This WordPress account is linked to a different Microsoft account. Please contact an administrator." msgstr "" -#: includes/class-m365-login-auth.php:858 +#: includes/class-m365-login-auth.php:937 msgid "You are not allowed to sign in with this account." msgstr "" -#: includes/class-m365-login-auth.php:859 +#: includes/class-m365-login-auth.php:938 msgid "Your Microsoft account is not a member of a group that is allowed to sign in here." msgstr "" -#: includes/class-m365-login-auth.php:860 +#: includes/class-m365-login-auth.php:939 msgid "Your group membership could not be verified. Please contact an administrator." msgstr "" -#: includes/class-m365-login-auth.php:861 +#: includes/class-m365-login-auth.php:940 +msgid "Your Microsoft account is a member of a group that is not allowed to sign in here." +msgstr "" + +#: includes/class-m365-login-auth.php:941 msgid "The fallback key is not valid." msgstr "" -#: includes/class-m365-login-auth.php:862 +#: includes/class-m365-login-auth.php:942 msgid "Too many attempts. Please wait 15 minutes." msgstr "" -#: includes/class-m365-login-auth.php:863 +#: includes/class-m365-login-auth.php:943 msgid "Too many sign-in attempts from your connection. Please wait a few minutes and try again." msgstr "" -#: includes/class-m365-login-auth.php:864 includes/class-m365-login-sync.php:1520 +#: includes/class-m365-login-auth.php:944 includes/class-m365-login-sync.php:1520 msgid "This account has been deactivated." msgstr "" @@ -1185,51 +1205,51 @@ msgstr "" msgid "Microsoft 365 group" msgstr "" -#: includes/class-m365-login-settings.php:54 +#: includes/class-m365-login-settings.php:55 msgid "Sign in with Microsoft" msgstr "" -#: includes/class-m365-login-settings.php:63 +#: includes/class-m365-login-settings.php:64 msgid "or" msgstr "" -#: includes/class-m365-login-settings.php:225 includes/class-m365-login-settings.php:530 +#: includes/class-m365-login-settings.php:226 includes/class-m365-login-settings.php:540 msgid "The private key could not be encrypted. Is the OpenSSL extension available?" msgstr "" -#: includes/class-m365-login-settings.php:476 +#: includes/class-m365-login-settings.php:486 msgid "The tenant ID must be a GUID (e.g. 1a2b3c4d-…) or one of \"organizations\", \"common\", \"consumers\"." msgstr "" -#: includes/class-m365-login-settings.php:484 +#: includes/class-m365-login-settings.php:494 msgid "The application (client) ID must be a GUID." msgstr "" -#: includes/class-m365-login-settings.php:496 +#: includes/class-m365-login-settings.php:506 msgid "The client secret contains invalid characters." msgstr "" -#: includes/class-m365-login-settings.php:500 +#: includes/class-m365-login-settings.php:510 msgid "The client secret could not be encrypted. Is the OpenSSL extension available?" msgstr "" -#: includes/class-m365-login-settings.php:520 +#: includes/class-m365-login-settings.php:530 msgid "Please paste both the private key and the certificate." msgstr "" -#: includes/class-m365-login-settings.php:522 +#: includes/class-m365-login-settings.php:532 msgid "The pasted key or certificate is too large." msgstr "" -#: includes/class-m365-login-settings.php:539 +#: includes/class-m365-login-settings.php:549 msgid "Certificate authentication is selected but no certificate is stored yet. Generate one or paste your own; the Microsoft button stays hidden until then." msgstr "" -#: includes/class-m365-login-settings.php:571 +#: includes/class-m365-login-settings.php:582 msgid "The custom login page must be a URL on this site." msgstr "" -#: includes/class-m365-login-settings.php:678 +#: includes/class-m365-login-settings.php:689 msgid "User sync: \"Delete\" needs a user who receives the posts of deleted accounts. Until one is selected, accounts are deactivated instead." msgstr "" diff --git a/readme.txt b/readme.txt index be2a63c..7decb55 100644 --- a/readme.txt +++ b/readme.txt @@ -21,6 +21,7 @@ The plugin is deliberately small and strict: * **Password login stays available.** The button is an additional option; the normal form is untouched. * **Fully customisable button.** Change the text, replace the Microsoft logo with your own icon from the media library, pick background, hover, text and border colours, adjust the corner radius, and choose whether the button appears above or below the login form – with a live preview. * **Entra group restriction.** Search and pick the groups whose members may sign in, right in the settings screen. Membership is checked via the ID token's `groups` claim or Microsoft Graph (nested groups included). +* **Excluded groups.** Members of the groups you exclude can never sign in with Microsoft, even if they are in an allowed group (checked with Microsoft Graph, fails closed). * **Button-only mode.** Hide the username/password form and refuse password sign-ins on the login page. A secret fallback link (and a `wp-config.php` constant) brings the form back when you need it. * **Clean settings screen** with a copy-and-paste redirect URI, a tenant connectivity test and a five-step setup guide. * **Custom login pages.** The button is added to every `wp_login_form()` form automatically; a shortcode and a template function cover page builders and theme templates. Point the plugin at your login page and error messages, the fallback link and the post-logout redirect go there instead of wp-login.php. @@ -67,7 +68,7 @@ Endpoints used (all under `https://login.microsoftonline.com/`): When the optional **group restriction** or the optional **user sync** is used, the plugin additionally connects to **Microsoft Graph** (`https://graph.microsoft.com/v1.0/`) using an application token obtained from `/{tenant}/oauth2/v2.0/token` (client credentials, client ID and secret or signed assertion are sent): * `/groups` – only when an administrator searches for groups in the settings screen. The typed search text is sent. -* `/users/{id}/checkMemberGroups` – during sign-in when the ID token carries no usable `groups` claim. The user's Microsoft object ID and the configured group IDs are sent; Microsoft returns which of those groups the user belongs to. +* `/users/{id}/checkMemberGroups` – during sign-in when the ID token carries no usable `groups` claim, and on every sign-in when excluded groups are configured. The user's Microsoft object ID and the configured group IDs are sent; Microsoft returns which of those groups the user belongs to. * `/users`, `/groups/{id}/transitiveMembers`, `/users/{id}` – only while the user sync runs (manually, on the configured schedule or via WP-CLI). The configured group IDs and the object IDs of linked accounts are sent; Microsoft returns the users with their account status and the profile attributes selected in the settings. * `/$batch` with `/users/{id}/photo`, and `/users/{id}/photos/240x240`, `/users/{id}/photo` – only while the user sync runs and "Profile photo" is selected. Returns the version and, when it changed, the image of the user's profile photo. @@ -153,6 +154,8 @@ The settings, cached data, the sync report and schedule, stored profile photos a * New: deactivate or delete WordPress accounts whose Microsoft 365 account was disabled or deleted; automatic reactivation; dry run, safety stop and protected administrators. * New: "Microsoft 365" column, deactivate/reactivate row actions and a read-only Microsoft 365 section on the profile screen. * New: `wp m365-login sync [--dry-run]` WP-CLI command and scheduled sync via WP-Cron. +* New: excluded Entra groups – their members can never sign in with Microsoft. +* Fix: failed Microsoft sign-ins (e.g. expired secret, group not allowed) ended in a PHP fatal error instead of the error message. * Fix: generating or removing the certificate in the settings did not keep the change and broke a stored client secret. = 1.0.0 =