WP Manifestindependent plugin directory
manifest / users / kaae-microsoft-login

KAAE Microsoft Login

KAAE Microsoft sign-in, staff directory, Subscriber enrollment, and site access controls.

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/akamomer/kaae-microsoft-login/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

KAAE Microsoft Login — 0.5.1

One plugin for separate WordPress installations. Each site's users, settings and Microsoft connections remain independent. Development has not enabled the new onboarding/exception settings, granted Microsoft consent, or created production users.

v0.5.1: Fill missing staff names

New staff accounts receive WordPress name fields as well as Display name. Directory enrollment uses Microsoft givenName and surname when available. If only a display name exists, the complete name goes into First name without guessing how to split it.

To repair previously enrolled accounts: update the plugin, open Users → KAAE Staff, Load directory from Microsoft again, select the existing staff, choose Sync names (fill missing) and Apply. Existing nonempty first/last names and custom display names are preserved. Missing fields are filled; no scheduled sync or overwrite is performed. Sync names never creates accounts, changes approval/block decisions, roles, emails, links, passwords or sessions. External accounts outside the KAAE directory are not affected. No additional Microsoft permissions are required.

v0.5.0: KAAE Staff directory and access management

Open Users → KAAE Staff (also linked from plugin settings). Staff management requires WordPress manage_options, list_users, create_users and edit_users permissions; individual existing accounts also require edit_user permission. This feature supports separate standalone WordPress installations.

  1. Add Microsoft Graph Delegated User.ReadBasic.All to the existing Entra app and obtain consent under KAAE policy. Keep User.Read. No Graph application, directory-write or mail permissions are needed.
  2. Connect your own WordPress administrator account to Microsoft and complete a Microsoft sign-in once using the current configuration.
  3. Select Load directory from Microsoft. The plugin starts a separate-purpose OAuth transaction bound to the original WordPress administrator/session, using the same registered callback URL, state cookie, nonce and PKCE. The returned tenant/object ID must match the administrator's already-linked identity. Permissions and the original session are checked again before loading the directory. Normal staff sign-ins do not request User.ReadBasic.All.
  4. The directory lists display name, canonical sign-in UPN, mailbox email, WordPress role and access status. Search and select up to 50 people per page, then apply Enroll / approve, Block access and end sessions, or Unblock access.
  5. Enroll your own administrator and required staff first. If you want only your selection to have access, enable Approved staff only in plugin settings. Enabling is refused unless your administrator's linked identity is approved and its Microsoft sign-in has been tested. The setting defaults off, and old settings forms preserve its saved value. No users are automatically approved just by viewing/loading the directory.

Enroll / approve uses the existing identity/email conflict rules and enrollment lock. New WordPress users receive Subscriber and a random password with local password authentication disabled; existing roles and passwords are preserved. Selecting a directory identity binds the account to its immutable tenant/object ID. Enroll does not remove an existing block.

Block saves a local per-identity block, marks any matching WordPress account, and destroys its WordPress session tokens. It denies Microsoft/password/application-password authentication and existing-cookie access regardless of automatic-account or approved-only settings. Blocking an unenrolled person creates no account and prevents later automatic enrollment. You cannot block your own WordPress administrator account or its connected Microsoft identity. Blocks apply to this site, not Microsoft or another WordPress installation.

Unblock removes the local block and restores the prior approval state; it never grants approval to someone previously unapproved. Roles remain unchanged. For bulk operations, invalid/expired selections reject the entire request before actions; per-person database/conflict failures are reported individually alongside a completed count. POST, capabilities, nonce and current administrator/session directory snapshot are required. Per-identity MySQL advisory locks prevent concurrent enrollment overwriting a block.

Approved staff only rejects unapproved KAAE Microsoft identities before matching/creating accounts and denies unapproved staff's existing cookies and other WordPress authentication paths. It also applies to linked staff whose email changes. External users without a Microsoft connection remain subject to the existing password-exception/site policy. The server-side KAAE_MS_PASSWORD_RECOVERY override also bypasses approval/block restrictions for emergency recovery. Do not enable the policy until your administrator and required staff are approved.

Directory reads use GET /v1.0/users with only id, displayName, mail and userPrincipalName. Results are restricted to canonical @kaae.org UPNs; basic directory permission cannot establish Member/Guest status. All sign-ins request delegated User.Read (already required for automatic accounts), and actual sign-in verifies Member status through /me for directory-enrolled staff even when automatic accounts and approved-only mode are both off. Normal staff are never asked for User.ReadBasic.All. Lists follow Microsoft nextLink paging only over HTTPS to graph.microsoft.com/v1.0/users, with redirects disabled and page/size limits (50 pages / 5,000 matching people). Invalid, looping or cross-host paging fails closed without saving a partial new snapshot. Upstream directory changes may have replication delays. Sanitized profiles are cached for 15 minutes, scoped to the administrator's WordPress browser session and current tenant/client/secret configuration. No access or refresh token is stored. A stale snapshot cannot be used by another administrator/session or after credential changes.

Installation/updating does not enroll, block, approve, or change a saved access policy. These changes occur only through the corresponding administrator controls. Each site keeps independent access records.

Additional offline verification: php tests/staff.php, php tests/staff.php recovery, and callback scenarios blocked, unapproved, unapproved-new, approved, directory, directory-wrong-account, directory-wrong-session. These cover delegated paging, cache isolation, no token persistence, enrollment roles, self-block protection, session revocation, policy enforcement, stale/forged selections, permissions/nonces and the administrator OAuth binding. The redirect/start suite now includes a thirteenth check for administrator-only directory scopes/session binding. Live directory consent/loading and staff management still require administrator testing after update.

v0.4.4: Refresh GitHub metadata from WordPress Check again

Dashboard → Updates → Check again now clears both the plugin's GitHub release cache and WordPress's plugin-update cache before WordPress performs its check. Previously, WordPress could keep reporting an older GitHub release for up to fifteen minutes even after the administrator requested a fresh check. Ordinary dashboard visits retain caching. The early hook requires update_plugins permission and only refreshes update metadata; it does not install anything or change authentication settings. The plugin's own Check for updates button continues to work.

v0.4.3: Return to the requested page after Microsoft sign-in

WordPress's redirect_to is validated with wp_validate_redirect, URL-encoded into the Microsoft sign-in button's local start link, and saved in the existing browser-bound ten-minute OAuth transaction. After authentication the stored value is validated again and passed to wp_safe_redirect. Missing, empty, non-string and disallowed destinations fall back to this installation's admin URL. Callback query parameters cannot choose the final destination. Existing in-flight transactions without a saved destination also fall back to the admin URL. For example, a sign-in started from /reports/?status=open&team=Finance%20%26%20Admin returns to that reports page with its filters. The registered Microsoft callback URI, PKCE, nonce/state verification, account permissions and profile-connection redirect remain unchanged.

Account and password policies

  • Manual connection (default): existing users sign in normally and connect their own KAAE Microsoft account from Users → Profile. Their WordPress email must match the Microsoft sign-in address and end exactly in @kaae.org.
  • Automatic staff accounts (opt-in): requests delegated Microsoft Graph User.Read, then reads /me?$select=id,userPrincipalName,userType over HTTPS. The directory object ID must match the validated ID token, userType must be Member, and the canonical userPrincipalName must end exactly in @kaae.org. Missing consent, Graph errors, guest accounts and unrelated domains fail closed. Mailbox aliases and the token email claim are not used for automatic matching.
  • For a first automatic sign-in, an exact existing WordPress email match is linked without changing roles, passwords, content or profile. New users are created as Subscriber, explicitly overriding WordPress's default registration role, with a random password and password authentication disabled. No bulk import or Microsoft-to-WordPress role synchronization occurs.
  • Once linked, users are identified by immutable tenant:object ID, not by an email lookup. Existing links are never automatically rebound to a different Microsoft identity. Multiple matching users/identities are rejected. MySQL/MariaDB advisory locks serialize enrollment and release on connection termination. If the database cannot provide GET_LOCK, automatic enrollment fails closed. Automatic enrollment supports standalone WordPress, not multisite.
  • KAAE tenant administrators are trusted to manage directory membership, UPNs and address reassignment. Automatic matching can connect privileged existing users, preserving their privileges. Review ownership of old/unlinked WordPress accounts before enabling automatic matching. Reassigning an address never transfers an already-linked identity automatically.
  • Personal password restriction: a user can select Disable password sign-in for my account in Users → Profile, then Update Profile. They must first connect and complete Microsoft authentication using the current credential configuration. The personal restriction blocks password and application-password authentication. It can be cleared by the same user but cannot override the site's Microsoft-only rule.
  • Site-wide Microsoft-only mode: blocks WordPress password authentication for everyone except the administrator's explicit list of existing external users. Enter one external user's email per line in settings; exceptions are saved as WordPress user IDs. KAAE addresses are not eligible. Exceptions do not create users or grant permissions, and stop allowing passwords if an account becomes a KAAE address. A personal password restriction takes precedence. Application passwords remain disabled for everyone while Microsoft-only mode is on, including external accounts. The previously saved single-user exception is honored immediately without a database migration and appears in the list. Saving the list replaces it atomically: any invalid entry rejects the entire save. Removing an email revokes that user's exception for future logins; an explicitly empty list removes all exceptions. Old settings forms remain accepted, and missing exception fields preserve the current list. Existing sessions are not revoked.
  • With an eligible external exception, the standard password form stays visible alongside Microsoft sign-in; password attempts by staff and unselected external users are rejected by the authentication gate. Password reset is allowed only for password-eligible users. Without an exception, the Microsoft-only screen replaces the normal login form. Login-page registration stays blocked under enforcement.
  • Disconnecting is blocked while site-wide enforcement or a personal password restriction applies. Existing sessions are not revoked by enabling a restriction. Microsoft/Graph outages do not automatically restore passwords.

Microsoft setup

Settings → KAAE Microsoft Login contains a six-step guide and the exact redirect URI for the current installation. The guide is collapsed by default; click “Microsoft setup — start here” to expand it. It uses the browser's native details control, which also supports keyboard activation without JavaScript.

  1. Register a single-tenant application in KAAE's Microsoft Entra workforce directory. Add Web redirect URIs copied from each site's plugin settings. Typical standard URLs are https://kaae.org/wp-login.php?action=kaae_ms_callback and https://form.kaae.org/wp-login.php?action=kaae_ms_callback; canonical www prefixes or installation paths can change these, so use the displayed values.
  2. Copy Directory (tenant) ID, Application (client) ID and a client-secret Value, not Secret ID. Keep secrets outside source control/chat. The same app can contain both Web redirect URIs; each WordPress site is configured separately. Do not enable implicit grant or public client flows for this server-side flow.
  3. For automatic staff accounts, add Microsoft Graph Delegated User.Read and obtain consent according to KAAE policy before enabling the setting. No mail, calendar, directory-wide or Graph application permissions are needed. Application assignment and Conditional Access must permit intended staff. Directory verification is performed on each Microsoft sign-in while automatic mode is enabled, including already-linked users.
  4. Save settings. Keep a logged-in administrator session open and test Microsoft sign-in in another browser. In manual mode, connect accounts before requiring Microsoft. In automatic mode, test both existing email matching and new Subscriber provisioning. Configure/test the external password exception before relying on it. Then enforce Microsoft-only sign-in if desired.

HTTPS and PHP OpenSSL are required. The confidential-client authorization code flow uses PKCE, browser-bound state, a ten-minute transaction, nonce, and strict RS256 signature/issuer/tenant/audience/time validation. Signing keys come only from Microsoft's tenant endpoint. Access tokens are used transiently for Graph and are not saved; refresh tokens are not requested. Secrets entered in the UI are stored in the WordPress database with autoload disabled and never rendered back. Optional deployment-managed KAAE_MS_TENANT, KAAE_MS_CLIENT and KAAE_MS_SECRET constants override UI values.

This version accepts client secrets only. Microsoft recommends certificate/federated credentials for production; those are not implemented. Manage secret expiration and rotation through your organization's credential process. Credential changes invalidate the recorded Microsoft-login test used to authorize personal/site password restrictions.

Recovery and compatibility

Emergency server recovery: temporarily set define( 'KAAE_MS_PASSWORD_RECOVERY', true ); in protected WordPress configuration before WordPress loads, or deactivate the plugin through server administration. The override disables both site-wide and personal restrictions, restoring normal WordPress password/application-password policy. Remove it after recovery. No recovery override or configuration edit is performed by development. There is no URL bypass. Settings/links remain after deactivation.

WordPress wp_authenticate_user, authenticate and wp_login hooks participate, preserving existing security plugins' opportunity to challenge/block authentication. Test Microsoft MFA, WordPress two-factor continuation, and security plugins on staging. Plugins implementing independent authentication outside standard WordPress hooks need separate review. Personal/API restrictions apply to new authentication, not already-issued WordPress sessions. External-user login retains ordinary WordPress password checking and security challenges; the exception is not a login bypass.

GitHub releases

Repository: https://github.com/akamomer/kaae-microsoft-login . Public GitHub releases are supported; private-repository authentication is not implemented. The official repository is used automatically when the repository field is blank. Enable the plugin's standard WordPress automatic updates when approved. Repository settings are not written into the site database by development.

Check for updates is available at the top of Settings → KAAE Microsoft Login and in the plugin's Installed Plugins action links. It refreshes the GitHub cache and WordPress's update check, then shows whether an update is available, the plugin is current, no published release exists, or the check failed. It does not install an update or alter sign-in settings. Installation uses WordPress's standard Updates screen; automatic installation uses WordPress's auto-update preference. This action requires administrator settings/update permissions and a valid nonce. Draft releases are not offered as updates.

The Test and publish release GitHub Actions workflow runs the offline tests, PHP lint and package build for main-branch pushes and pull requests. Successful main-branch runs publish a versioned release with the installable ZIP automatically, as authorized by the site owner. Pull requests cannot create releases. The workflow uses GitHub's temporary repository token; no personal access token or WordPress/Microsoft secret is required. Published releases are left unchanged, so bump the plugin version for each new release. It does not directly deploy to either WordPress server; WordPress handles installation according to each site's update settings.

No manual GitHub publication is needed. New packages are uploaded privately before the release is made public. An existing draft for the current version is published only if it has a complete ZIP and its source matches the tested code (changes solely to release automation/documentation are allowed). Runtime code changes require a new version; existing published packages are never overwritten. The workflow can also be run from Actions → Test and publish release → Run workflow. GitHub-hosted CI currently tests its supplied PHP runtime; the minimum PHP 8.1 runtime has also been checked locally.

  1. Keep this folder as the repository root; exclude site credentials/data.
  2. Update the PHP header version and this document, run offline checks and staging validation, and publish a matching stable tag, for example v0.3.0.
  3. Run python3 tools/build-release.py --output-dir /tmp/kaae-ms-release. Upload the resulting kaae-microsoft-login.zip as that release's named asset. It contains one top-level kaae-microsoft-login/ folder with runtime PHP and this README. Tests/build tools are excluded. Automatically generated GitHub source archives are not used.
  4. Enable normal WordPress automatic updates for the plugin on each site when approved. The plugin does not force opt-in. WP-Cron, update policy and filesystem permissions control scheduling/installation. Updates are not simultaneous. Only stable published releases with the exact ZIP asset are offered; plugin release metadata is cached for 15 minutes in addition to WordPress's check schedule.

Verification

Offline (no production WordPress, database, or network): php tests/run.php, php tests/updates.php, php tests/redirects.php, php tests/policy.php, php tests/policy.php recovery, php tests/accounts.php, python3 tests/release.py, and PHP syntax checks. Run php tests/callback.php SCENARIO for each of existing, new, manual, guest, wrong-tenant, wrong-nonce, conflicting, redirect-report, redirect-relative, redirect-external, redirect-array, redirect-query-only, redirect-query-injection, and profile-link. These callback scenarios use locally signed tokens with in-memory Microsoft and WordPress doubles. The 12 redirect helper/button/start regression cases and callback scenarios cover filtered report URLs, correct query encoding, browser-bound ten-minute storage, unsafe/non-string/missing destination fallbacks, callback-query injection, revalidation and unchanged profile linking/PKCE/callback URI. The suite also covers signatures/claims, release validation, update-control permissions/cache refresh/result messages, personal policies, administrator/nonce protection, multiple external exceptions, legacy-setting compatibility, removal and atomic validation, directory membership/object/domain checks, existing-role preservation, conflict rejection, Subscriber creation, enrollment locks, auth-cookie/login-hook completion, and failure paths.

Staging/live validation still required: correct app/redirect/consent; manual connection; automatic existing-user and new-user login; role preservation; matching aliases vs canonical UPN; duplicate/different identity rejection; guest/wrong-domain/wrong-tenant rejection; nonce/state/cookie/replay handling; staff password/API rejection; external password login and reset; personal opt-in and disconnect blocking; expired secrets; two-factor continuation; server recovery; and a versioned ZIP update retaining the plugin folder name. No production test accounts or login events are generated by the offline tests.

Sources: https://learn.microsoft.com/en-us/entra/identity-platform/claims-validation ; https://learn.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0 ; https://learn.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0 ; https://learn.microsoft.com/en-us/entra/identity-platform/how-to-add-credentials ; https://developer.wordpress.org/reference/hooks/update_plugins_hostname/