Add certificate authentication, in-app setup guides and security audit

Certificate (RFC 7523 client assertion) as an alternative to the client
secret: one-click generation of a 3072-bit RSA key pair with a
self-signed certificate, .cer download (public part only), own PEM
upload with validation, expiry display, encrypted key storage. Both the
authorization code exchange and the Graph client-credentials request
use the selected method. Step-by-step guides for secret, certificate
and the app registration are shown in the settings.

Security audit (docs/security-audit.md) and fixes:
- Multi-tenant mode ignored the unverified email claim: matching now
  uses the UPN only, or the email claim when xms_edov is true.
- Login starts are rate limited per client (30 per 10 minutes).
- Optional trusted proxy header for client IPs
  (M365_LOGIN_CLIENT_IP_HEADER / filter).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJxAHYdMfKPoN4koRc4Ci2
This commit is contained in:
friloo 2026-09-22 15:04:32 +00:00
parent 966164177d
commit 8766927123
No known key found for this signature in database
19 changed files with 2515 additions and 644 deletions

View file

@ -33,7 +33,10 @@ The plugin is deliberately small and strict:
* **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.
* The **client secret is encrypted at rest** (AES-256-GCM, key derived from your WordPress salts) and never displayed again.
* **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 =
@ -71,7 +74,7 @@ Microsoft terms and privacy: [Microsoft Services Agreement](https://www.microsof
2. Go to **Settings → M365 Login** 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. Under **Certificates & secrets** create a client secret and paste its *value* 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.
@ -79,6 +82,10 @@ Make sure every user who should be able to sign in has the same e-mail address i
== 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.