- 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>
8.3 KiB
Changelog
All notable changes to this project are documented in this file. The format follows Keep a Changelog and the plugin adheres to Semantic Versioning.
[1.1.0] – 2026-09-23
Added
-
User sync (new "User sync" tab): imports Microsoft 365 / Entra ID users as WordPress accounts – the whole tenant or the (nested) members of selected groups, guests optional – and links existing accounts by e-mail address.
-
Default role plus group → role mapping with a Graph-powered group picker; mapped roles either added to or replacing the default role (first match wins, reorderable). Roles of accounts that existed before the sync are only managed on request.
-
Selectable profile attributes (display name, first/last name, job title, department, company, office, employee ID, phones, address, language) and the profile photo, which replaces the Gravatar.
-
Profile photos follow Microsoft 365 on every run: versions are compared via Graph
$batch(20 users per request), changed photos are downloaded again (old file deleted, new URL), photos deleted in Microsoft 365 are deleted in WordPress. Errors never delete a photo. -
Deselected profile fields (
m365_*) and photos are removed from the profiles on the next run; fields cleared in Microsoft 365 are cleared in WordPress; a user's photo is deleted together with the user. -
Deactivation or deletion of WordPress accounts whose Microsoft 365 account was disabled, deleted or removed from the sync groups; automatic reactivation. Deactivated accounts cannot sign in at all (Microsoft, password, application passwords) and lose all sessions.
-
Safeguards: dry run, safety stop above 20 % deprovisioning (at least 5 accounts), abort on any Graph error, deletion only on a 404 for the object ID, protected pre-existing administrators and own account, content reassignment required for deletion, run lock.
-
Scheduled sync via WP-Cron (hourly, twice daily, daily),
wp m365-login sync [--dry-run], report of the last run in the settings. -
"Microsoft 365" column and deactivate/reactivate row actions on the users screen; read-only Microsoft 365 section on the profile screen.
-
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
groupsclaim and always via Microsoft GraphcheckMemberGroups(a filtered claim cannot prove non-membership); fails closed.
Changed
-
The group picker is reusable (security groups, sync groups, role mapping).
-
"Link Microsoft account" on the profile screen (the signed-in user binds their own Microsoft account) and an administrator-assigned Microsoft account (UPN) per user – for administrators whose user principal name differs from their e-mail address. Sign-in finds bound accounts by object ID first, then by assigned UPN, then by e-mail.
-
Privacy exporter and eraser for the data the plugin copies.
Changed
- No frame around the login box (form and Microsoft block) on wp-login.php.
Fixed
- Button-only mode: the "Lost your password?" link is removed server-side, the lost-password and reset screens redirect to the login page and password resets are refused (unless the fallback link is active); the login stylesheet is also loaded while the connection is broken, so the password form stays hidden.
- Accounts whose WordPress e-mail is the user principal name (while the Microsoft mail differs) were not found at sign-in and got a duplicate account from the sync; sign-in and sync now try the mail address and the UPN.
Security
Third audit (details: docs/security-audit.md, section 7): XML-RPC system.multicall bypass of button-only mode closed; privileged-account rules extended (bind_oid off, multisite-wide capabilities, code/HTML capabilities, deactivated administrators); cookie protection on WordPress 6.0/6.1; atomic and refreshed run lock with crash report; per-account tenant for deprovisioning; demotion safety stop; safety stop based on accounts linked before the run; photos re-encoded and removed on deactivation; one-time hardening of accounts deactivated by 1.0.
Fixes from a full second security audit (details: docs/security-audit.md, section 6):
- Multisite: settings, connection test, certificates and user sync require
manage_network_options(a site admin could otherwise sign in as the super admin via an own tenant). - Administrator accounts are only linked (sync and first sign-in) through a matching user principal name of a member account, never through the freely settable
mailattribute; their e-mail address is never changed by the sync; e-mail change notifications stay on for other accounts. - Button-only mode: exemption by credential (application passwords, WP-CLI) instead of request context – closes bypasses through
xmlrpc.phpand REST login handlers of other plugins; API requests never receive login cookies; same message for right and wrong passwords; mode no longer switches off when the connection breaks; fallback cookie expires on the server; the correct fallback key works despite IP lockouts; right-most proxy header entry; start limit raised to 300. - Multi-tenant mode refuses guest/external identities; one Microsoft object ID can only be bound to one WordPress account.
- Deactivation also sets a random password, revokes application passwords and removes the role (restored on reactivation); linked people disabled in Microsoft 365 are deactivated even if their e-mail vanished or changed domain; duplicate bindings are all deprovisioned.
- Sync safety: abort on an empty directory answer, no deprovisioning in the first run after a tenant change, atomic run lock, strict photo path validation.
- Certificates: key+certificate bundles are refused in the certificate field and only a clean re-exported certificate is stored.
- Robust sanitising of array input, encoded
redirect_toin the button URL, separate nonces per AJAX action, escaped role lists, no Graph retries/sleeps during sign-in, warnings for public Microsoft 365 groups, group rules in multi-tenant mode and salts missing from wp-config.php, cached Graph token removed on uninstall.
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
Added
- "Sign in with Microsoft" button on
wp-login.php(OpenID Connect authorization code flow with PKCE). - Matching of existing WordPress users by e-mail address (optional UPN fallback), no user provisioning.
- Settings screen (own top-level menu entry "M365 Login") with connection, button and security tabs, live button preview, colour presets, media-library icon picker, redirect-URI copy button and tenant connectivity test.
- ID token verification against Microsoft's JWKS (RS256, issuer, audience, tenant, expiry, nonce).
- Encrypted client secret storage (AES-256-GCM).
- Certificate based client authentication (RFC 7523
private_key_jwt): one-click generation of a 3072-bit RSA key pair with self-signed certificate,.cerdownload, own PEM upload, expiry display; step-by-step guides for both methods in the settings. - Security audit (docs/security-audit.md) with fixes: unverified
emailclaim ignored in multi-tenant mode, login-start rate limit, trusted proxy IP header. - Account binding to the Microsoft object ID, e-mail domain allow-list.
- Entra group restriction with a Graph-powered group picker; membership verified via the
groupsclaim or Microsoft GraphcheckMemberGroups. - Button-only mode that hides the password form and blocks password sign-in on
wp-login.php, with a secret, rate-limited fallback link and awp-config.phpemergency constant. - Custom login page support: automatic button in
wp_login_form()forms,m365_login_button()/m365_login_messages()template functions, custom login URL for error messages, fallback link and logout redirect. [m365_login_button]shortcode (withdividerandmessagesattributes) and developer hooks.- German translation.