Adds an "M365 Login" menu entry with a Microsoft-style icon and one submenu per tab (Connection, Button, Security). The active tab is taken from the URL, kept in the post-save redirect and highlighted in the submenu. Notices are printed explicitly because top-level pages do not include options-head.php. Links, docs and screenshots updated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJxAHYdMfKPoN4koRc4Ci2
141 lines
11 KiB
Text
141 lines
11 KiB
Text
=== M365 Login ===
|
||
Contributors: friloo
|
||
Tags: microsoft, entra id, azure ad, sso, login
|
||
Requires at least: 6.0
|
||
Tested up to: 6.9
|
||
Requires PHP: 7.4
|
||
Stable tag: 1.0.0
|
||
License: GPLv2 or later
|
||
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||
|
||
Adds a customisable "Sign in with Microsoft" button to the login page. Existing users sign in with their Microsoft 365 / Entra ID account, matched by e-mail address.
|
||
|
||
== Description ==
|
||
|
||
**M365 Login** lets your existing WordPress users sign in with their Microsoft 365 (Microsoft Entra ID, formerly Azure AD) work or school account. It adds a button to the standard WordPress login screen and uses the OpenID Connect authorization code flow with PKCE.
|
||
|
||
The plugin is deliberately small and strict:
|
||
|
||
* **No user provisioning.** A Microsoft sign-in succeeds only when a WordPress user with the same e-mail address already exists. Nobody gets an account just by having a Microsoft login.
|
||
* **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).
|
||
* **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.
|
||
|
||
= Security =
|
||
|
||
* OpenID Connect **authorization code flow with PKCE (S256)** – tokens are exchanged server-to-server and never pass through the browser.
|
||
* **Single-use state and nonce** values, bound to the browser with an HttpOnly, SameSite cookie (CSRF and replay protection).
|
||
* The **ID token signature is verified** against Microsoft's published signing keys (JWKS, cached and refreshed on key rollover). Issuer, audience, tenant, expiry, not-before and nonce are all checked. Only RS256 is accepted.
|
||
* Optional **tenant pinning**: when a tenant GUID is configured, tokens from any other tenant are rejected.
|
||
* **Account binding**: on first sign-in the immutable Microsoft object ID is stored with the user; later sign-ins with the same e-mail but a different Microsoft identity are refused.
|
||
* Optional **e-mail domain allow-list** and **group allow-list** (fails closed when membership cannot be verified).
|
||
* **Button-only mode** blocks password sign-in server-side, not just visually; the fallback key is rate limited and never stored in a cookie.
|
||
* **Certificate authentication** (RFC 7523 client assertion) as an alternative to a client secret: generate a 3072-bit key pair in the settings, upload only the public certificate to Entra ID. Nothing secret is ever transmitted.
|
||
* The **client secret / private key is encrypted at rest** (AES-256-GCM, key derived from your WordPress salts) and never displayed again.
|
||
* In multi-tenant mode the unverified `email` claim is ignored; matching uses the user principal name (verified domain) only.
|
||
* Login starts and fallback-key attempts are rate limited per client.
|
||
* Every setting is sanitised, every output escaped, every admin request nonce- and capability-checked.
|
||
|
||
= Developer hooks =
|
||
|
||
* `m365_login_show_button` – filter, hide the button conditionally.
|
||
* `m365_login_authorize_params` – filter the parameters sent to Microsoft (e.g. `domain_hint`).
|
||
* `m365_login_match_email` – filter the e-mail address used for the lookup.
|
||
* `m365_login_allow_user` – filter, return `false` to block a matched user (e.g. group checks).
|
||
* `m365_login_success` – action after a successful sign-in, receives the user and verified claims.
|
||
* `m365_login_block_password_login` – filter, return `false` to exempt a password sign-in from button-only mode.
|
||
|
||
== External services ==
|
||
|
||
This plugin connects to **Microsoft identity platform (Microsoft Entra ID)** to authenticate users. It is required for the plugin's only purpose – signing users in with their Microsoft account – and is only contacted when a user clicks the Microsoft button or when an administrator uses the "Test tenant" button.
|
||
|
||
Endpoints used (all under `https://login.microsoftonline.com/`):
|
||
|
||
* `/{tenant}/oauth2/v2.0/authorize` – the user's browser is redirected here to sign in at Microsoft. Microsoft receives the application (client) ID, the redirect URI of this site, a random state, nonce and PKCE challenge.
|
||
* `/{tenant}/oauth2/v2.0/token` – the server exchanges the authorization code for an ID token. Microsoft receives the client ID, client secret, the code and the PKCE verifier.
|
||
* `/{tenant}/discovery/v2.0/keys` – the server downloads Microsoft's public signing keys to verify the ID token. No user data is sent.
|
||
* `/{tenant}/v2.0/.well-known/openid-configuration` – fetched only when an administrator clicks "Test tenant". No user data is sent.
|
||
|
||
When the optional **group restriction** is configured, 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 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.
|
||
|
||
The plugin receives the user's e-mail address / user principal name, display name and Microsoft object ID from Microsoft and uses them solely to find the matching WordPress account. Nothing else is stored.
|
||
|
||
Microsoft terms and privacy: [Microsoft Services Agreement](https://www.microsoft.com/servicesagreement), [Microsoft Privacy Statement](https://privacy.microsoft.com/privacystatement), [Microsoft identity platform documentation](https://learn.microsoft.com/entra/identity-platform/).
|
||
|
||
== Installation ==
|
||
|
||
1. Upload the plugin folder to `/wp-content/plugins/` or install it through the WordPress plugin screen, then activate it.
|
||
2. Open the new **M365 Login** menu entry and copy the **Redirect URI** shown in the sidebar.
|
||
3. In the [Microsoft Entra admin center](https://entra.microsoft.com/) open **App registrations → New registration**. Choose *Accounts in this organizational directory only*, select the **Web** platform and paste the redirect URI.
|
||
4. From the app's overview page copy the **Application (client) ID** and the **Directory (tenant) ID** into the plugin settings.
|
||
5. Pick the authentication method: either generate a certificate in the plugin and upload the downloaded `.cer` under **Certificates & secrets → Certificates**, or create a client secret under **Certificates & secrets → Client secrets** and paste its *value*. Both methods have a step-by-step guide in the settings.
|
||
6. Under **Token configuration** add the optional claim **email** for ID tokens (recommended). The delegated permissions `openid`, `profile` and `email` are granted by default.
|
||
7. Save. The button now appears on `wp-login.php`. Customise it on the **Button** tab.
|
||
|
||
Make sure every user who should be able to sign in has the same e-mail address in WordPress as in Microsoft 365.
|
||
|
||
== Frequently Asked Questions ==
|
||
|
||
= Client secret or certificate? =
|
||
|
||
Both work. A certificate is recommended: the private key stays on your server (encrypted), only the public certificate is uploaded to Entra ID, and every token request is signed with a short-lived assertion instead of sending a shared secret. The plugin generates the certificate for you and shows its expiry date.
|
||
|
||
= Does the plugin create users? =
|
||
|
||
No. Users must already exist in WordPress. The e-mail address is the only link between the Microsoft account and the WordPress account. This is intentional – it keeps the administrator in control of who can access the site.
|
||
|
||
= Which accounts can sign in? =
|
||
|
||
Any Microsoft account of the configured tenant whose e-mail address (or user principal name) matches an existing WordPress user. Restrict it further with the e-mail domain allow-list or the `m365_login_allow_user` filter.
|
||
|
||
= Can I use it with personal Microsoft accounts (outlook.com)? =
|
||
|
||
Set the tenant to `consumers` or `common`. Note that Microsoft does not allow query strings in redirect URIs for apps that support personal accounts, so your site must use pretty permalinks (the callback URL is then `/m365-login/callback` without a query string).
|
||
|
||
= The sign-in fails with "No WordPress account exists for your Microsoft e-mail address" =
|
||
|
||
The e-mail address in the Microsoft token does not match any WordPress user. Check the user's e-mail address in WordPress, enable the UPN fallback on the Security tab, or add the `email` optional claim in the app registration.
|
||
|
||
= How do I restrict sign-in to certain Entra groups? =
|
||
|
||
Open the Security tab, search for the groups (requires the Microsoft Graph application permission `GroupMember.Read.All` with admin consent) or paste their object IDs, add them and save. During sign-in the plugin first looks at the `groups` claim of the ID token; if the app registration does not emit one (or the user is in more than 200 groups) it asks Microsoft Graph (`User.Read.All`). If membership cannot be verified, the sign-in is refused.
|
||
|
||
= How do I get back in when button-only mode is on and Microsoft sign-in is broken? =
|
||
|
||
Open the fallback link shown on the Security tab (`wp-login.php?m365_fallback=KEY`); the password form is shown again in that browser for 30 minutes. Without the link, add `define( 'M365_LOGIN_DISABLE_BUTTON_ONLY', true );` to `wp-config.php` or rename the plugin folder via FTP.
|
||
|
||
= Does it work with custom login pages? =
|
||
|
||
Yes. Forms rendered with `wp_login_form()` get the button automatically. For page builders use the shortcode `[m365_login_button redirect="/dashboard/" divider="yes"]`, in theme templates call `m365_login_button()`. Enter the page's URL on the Button tab so error messages and the fallback link point there. Button-only mode blocks password sign-ins from custom forms as well.
|
||
|
||
= Does it support multisite? =
|
||
|
||
Yes. Settings are per site; a user must be a member of the site (or a super admin) to sign in.
|
||
|
||
= What happens on uninstall? =
|
||
|
||
The settings, cached data and the per-user Microsoft object ID are removed.
|
||
|
||
== Screenshots ==
|
||
|
||
1. The customised button on the WordPress login screen.
|
||
2. M365 Login – Connection tab with redirect URI and tenant test.
|
||
3. M365 Login – Button tab with live preview, colour pickers and icon picker.
|
||
4. M365 Login – Security tab with the Entra group picker and button-only mode.
|
||
5. Login screen in button-only mode.
|
||
|
||
== Changelog ==
|
||
|
||
= 1.0.0 =
|
||
* Initial release.
|
||
|
||
== Upgrade Notice ==
|
||
|
||
= 1.0.0 =
|
||
Initial release.
|