Add M365 Login plugin: Microsoft Entra ID sign-in for existing users

Adds a WordPress plugin that places a customisable "Sign in with
Microsoft" button on wp-login.php and signs existing users in via the
OpenID Connect authorization code flow with PKCE. Users are matched by
e-mail address only; no accounts are created.

Security: single-use state/nonce bound to an HttpOnly cookie, ID token
signature verification against Microsoft's JWKS (RS256 only) with
issuer/audience/tenant/expiry/nonce checks, optional tenant pinning,
account binding to the Microsoft object ID, e-mail domain allow-list,
client secret encrypted at rest (AES-256-GCM).

Admin: settings screen with connection, button and security tabs, live
button preview, colour presets, media-library icon picker, redirect URI
copy button and tenant connectivity test.

Packaging for WordPress.org: readme.txt with External services section,
GPL-2.0 license, uninstall.php, POT + German translations, .distignore,
build script, PHPCS config and CI running Plugin Check.

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 14:21:10 +00:00
commit 3e3e87b399
No known key found for this signature in database
35 changed files with 5413 additions and 0 deletions

458
languages/m365-login.pot Normal file
View file

@ -0,0 +1,458 @@
# Copyright (C) 2026 friloo
# This file is distributed under the GPL-2.0-or-later.
msgid ""
msgstr ""
"Project-Id-Version: M365 Login 1.0.0\n"
"Report-Msgid-Bugs-To: https://github.com/friloo/wp-m365-login/issues\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"POT-Creation-Date: 2026-09-22T00:00:00+00:00\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"X-Generator: bin/make-pot.py\n"
"X-Domain: m365-login\n"
#: includes/class-m365-login-admin.php:63 includes/class-m365-login-admin.php:64 includes/class-m365-login-admin.php:203
msgid "M365 Login"
msgstr ""
#: includes/class-m365-login-admin.php:102
msgid "M365 Login is active but not connected to Microsoft Entra ID yet."
msgstr ""
#: includes/class-m365-login-admin.php:104
msgid "Open the settings"
msgstr ""
#: includes/class-m365-login-admin.php:132
msgid "Choose button icon"
msgstr ""
#: includes/class-m365-login-admin.php:133
msgid "Use this icon"
msgstr ""
#: includes/class-m365-login-admin.php:134
msgid "Copied!"
msgstr ""
#: includes/class-m365-login-admin.php:135 includes/class-m365-login-admin.php:427
msgid "Copy"
msgstr ""
#: includes/class-m365-login-admin.php:136
msgid "Testing…"
msgstr ""
#: includes/class-m365-login-admin.php:137
msgid "The tenant could not be reached. Check the tenant ID and the servers outgoing connections."
msgstr ""
#: includes/class-m365-login-admin.php:149
msgid "You are not allowed to do this."
msgstr ""
#: includes/class-m365-login-admin.php:154
msgid "Please enter a valid tenant ID first."
msgstr ""
#. translators: %d: HTTP status code
#: includes/class-m365-login-admin.php:168
msgid "Microsoft answered with HTTP %d. Is the tenant ID correct?"
msgstr ""
#. translators: %d: HTTP status code
#: includes/class-m365-login-admin.php:177
msgid "Tenant reachable. The OpenID configuration was loaded successfully."
msgstr ""
#: includes/class-m365-login-admin.php:187
msgid "You are not allowed to access this page."
msgstr ""
#: includes/class-m365-login-admin.php:204
msgid "Let existing users sign in with their Microsoft 365 / Entra ID account."
msgstr ""
#: includes/class-m365-login-admin.php:209
msgid "Connected"
msgstr ""
#: includes/class-m365-login-admin.php:209
msgid "Setup incomplete"
msgstr ""
#: includes/class-m365-login-admin.php:217
msgid "Connection"
msgstr ""
#: includes/class-m365-login-admin.php:218
msgid "Button"
msgstr ""
#: includes/class-m365-login-admin.php:219
msgid "Security"
msgstr ""
#: includes/class-m365-login-admin.php:228
msgid "Microsoft Entra ID app registration"
msgstr ""
#: includes/class-m365-login-admin.php:229
msgid "Enter the values from your app registration in the Microsoft Entra admin center."
msgstr ""
#: includes/class-m365-login-admin.php:232
msgid "Directory (tenant) ID"
msgstr ""
#: includes/class-m365-login-admin.php:235
msgid "Test tenant"
msgstr ""
#: includes/class-m365-login-admin.php:237
msgid "Recommended: the GUID of your tenant. Only sign-ins from this tenant are accepted. \"organizations\" allows any work or school account."
msgstr ""
#: includes/class-m365-login-admin.php:242
msgid "Application (client) ID"
msgstr ""
#: includes/class-m365-login-admin.php:247
msgid "Client secret"
msgstr ""
#: includes/class-m365-login-admin.php:249
msgid "•••••••••••• (stored, leave empty to keep)"
msgstr ""
#: includes/class-m365-login-admin.php:249
msgid "Paste the secret value"
msgstr ""
#: includes/class-m365-login-admin.php:250
msgid "Show secret"
msgstr ""
#: includes/class-m365-login-admin.php:255
msgid "Remove the stored secret"
msgstr ""
#: includes/class-m365-login-admin.php:258
msgid "Stored encrypted (AES-256-GCM, key derived from your WordPress salts) and never displayed again. Client secrets expire note the expiry date in Entra ID."
msgstr ""
#: includes/class-m365-login-admin.php:262
msgid "Account prompt"
msgstr ""
#: includes/class-m365-login-admin.php:264
msgid "Always let the user pick an account (recommended)"
msgstr ""
#: includes/class-m365-login-admin.php:265
msgid "Use the current Microsoft session if available"
msgstr ""
#: includes/class-m365-login-admin.php:266
msgid "Always require re-entering credentials"
msgstr ""
#: includes/class-m365-login-admin.php:275
msgid "Appearance"
msgstr ""
#: includes/class-m365-login-admin.php:278
msgid "Live preview"
msgstr ""
#: includes/class-m365-login-admin.php:292
msgid "Button text"
msgstr ""
#: includes/class-m365-login-admin.php:296
msgid "Divider text"
msgstr ""
#: includes/class-m365-login-admin.php:298
msgid "Leave empty to hide the divider line."
msgstr ""
#: includes/class-m365-login-admin.php:303
msgid "Icon"
msgstr ""
#: includes/class-m365-login-admin.php:306
msgid "Show an icon on the button"
msgstr ""
#: includes/class-m365-login-admin.php:317
msgid "Default: Microsoft logo"
msgstr ""
#: includes/class-m365-login-admin.php:319
msgid "Choose from media library"
msgstr ""
#: includes/class-m365-login-admin.php:320
msgid "Use Microsoft logo"
msgstr ""
#: includes/class-m365-login-admin.php:322
msgid "PNG, SVG, JPG or WebP. Square images (e.g. 64×64 px) work best."
msgstr ""
#: includes/class-m365-login-admin.php:330
msgid "Background"
msgstr ""
#: includes/class-m365-login-admin.php:331
msgid "Background (hover)"
msgstr ""
#: includes/class-m365-login-admin.php:332
msgid "Text colour"
msgstr ""
#: includes/class-m365-login-admin.php:333
msgid "Border"
msgstr ""
#: includes/class-m365-login-admin.php:346
msgid "Corner radius"
msgstr ""
#: includes/class-m365-login-admin.php:350
msgid "Position on the login page"
msgstr ""
#: includes/class-m365-login-admin.php:352
msgid "Below the login form"
msgstr ""
#: includes/class-m365-login-admin.php:353
msgid "Above the login form"
msgstr ""
#: includes/class-m365-login-admin.php:359
msgid "Quick presets"
msgstr ""
#: includes/class-m365-login-admin.php:360
msgid "Microsoft dark"
msgstr ""
#: includes/class-m365-login-admin.php:361
msgid "Microsoft light"
msgstr ""
#: includes/class-m365-login-admin.php:362
msgid "Azure blue"
msgstr ""
#: includes/class-m365-login-admin.php:363
msgid "WordPress blue"
msgstr ""
#: includes/class-m365-login-admin.php:371
msgid "User matching & hardening"
msgstr ""
#: includes/class-m365-login-admin.php:372
msgid "Users are never created automatically. A Microsoft sign-in only succeeds when a WordPress user with the same e-mail address already exists."
msgstr ""
#: includes/class-m365-login-admin.php:377
msgid "Bind WordPress accounts to the Microsoft object ID"
msgstr ""
#: includes/class-m365-login-admin.php:378
msgid "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 rejected. Strongly recommended."
msgstr ""
#: includes/class-m365-login-admin.php:385
msgid "Fall back to the user principal name (UPN)"
msgstr ""
#: includes/class-m365-login-admin.php:386
msgid "If the token contains no \"email\" claim, use the UPN (e.g. jane@contoso.com) when it is a valid e-mail address. Usually required for work accounts."
msgstr ""
#: includes/class-m365-login-admin.php:393
msgid "Keep users signed in (\"Remember me\")"
msgstr ""
#: includes/class-m365-login-admin.php:394
msgid "Issues a 14-day WordPress session instead of a browser session."
msgstr ""
#: includes/class-m365-login-admin.php:399
msgid "Allowed e-mail domains (optional)"
msgstr ""
#: includes/class-m365-login-admin.php:401
msgid "One or more domains separated by commas or new lines. Leave empty to allow any domain of your tenant."
msgstr ""
#: includes/class-m365-login-admin.php:406
msgid "What the plugin does to keep sign-ins safe"
msgstr ""
#: includes/class-m365-login-admin.php:408
msgid "OpenID Connect authorization code flow with PKCE (S256) no tokens ever pass through the browser."
msgstr ""
#: includes/class-m365-login-admin.php:409
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:410
msgid "ID token signature verified against Microsofts published signing keys; issuer, audience, tenant, expiry and nonce are checked."
msgstr ""
#: includes/class-m365-login-admin.php:411
msgid "Client secret encrypted at rest; no accounts are created, no passwords are changed."
msgstr ""
#: includes/class-m365-login-admin.php:417
msgid "Save changes"
msgstr ""
#: includes/class-m365-login-admin.php:423
msgid "Redirect URI"
msgstr ""
#: includes/class-m365-login-admin.php:424
msgid "Register this URI in your app registration under Authentication → Web → Redirect URIs:"
msgstr ""
#: includes/class-m365-login-admin.php:430
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:433
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:438
msgid "Setup in 5 steps"
msgstr ""
#: includes/class-m365-login-admin.php:440
msgid "Open the Microsoft Entra admin center → App registrations → New registration."
msgstr ""
#: includes/class-m365-login-admin.php:441
msgid "Choose \"Accounts in this organizational directory only\", set the platform to Web and paste the redirect URI above."
msgstr ""
#: includes/class-m365-login-admin.php:442
msgid "Copy the Application (client) ID and Directory (tenant) ID from the overview page."
msgstr ""
#: includes/class-m365-login-admin.php:443
msgid "Under Certificates & secrets create a client secret and copy its value (not the ID)."
msgstr ""
#: includes/class-m365-login-admin.php:444
msgid "Under Token configuration add the optional claim \"email\" for ID tokens (recommended), then save this page."
msgstr ""
#: includes/class-m365-login-admin.php:446
msgid "Required API permission: openid, profile, email (delegated) granted by default."
msgstr ""
#: includes/class-m365-login-admin.php:450
msgid "Shortcode"
msgstr ""
#: includes/class-m365-login-admin.php:451
msgid "Place the button on a custom login page:"
msgstr ""
#: includes/class-m365-login-auth.php:593
msgid "Microsoft login is not configured yet."
msgstr ""
#: includes/class-m365-login-auth.php:594
msgid "The login request expired or was invalid. Please try again."
msgstr ""
#: includes/class-m365-login-auth.php:595
msgid "Microsoft sign-in was cancelled."
msgstr ""
#: includes/class-m365-login-auth.php:596
msgid "Microsoft returned an error. Please try again."
msgstr ""
#: includes/class-m365-login-auth.php:597
msgid "Could not complete the sign-in with Microsoft. Please try again or contact an administrator."
msgstr ""
#: includes/class-m365-login-auth.php:598
msgid "The Microsoft sign-in could not be verified."
msgstr ""
#: includes/class-m365-login-auth.php:599
msgid "Your Microsoft account did not provide an e-mail address."
msgstr ""
#: includes/class-m365-login-auth.php:600
msgid "Your e-mail domain is not allowed to sign in here."
msgstr ""
#: includes/class-m365-login-auth.php:601
msgid "No WordPress account exists for your Microsoft e-mail address."
msgstr ""
#: includes/class-m365-login-auth.php:602
msgid "This WordPress account is linked to a different Microsoft account. Please contact an administrator."
msgstr ""
#: includes/class-m365-login-auth.php:603
msgid "You are not allowed to sign in with this account."
msgstr ""
#: includes/class-m365-login-settings.php:40
msgid "Sign in with Microsoft"
msgstr ""
#: includes/class-m365-login-settings.php:49
msgid "or"
msgstr ""
#: includes/class-m365-login-settings.php:176
msgid "The tenant ID must be a GUID (e.g. 1a2b3c4d-…) or one of \"organizations\", \"common\", \"consumers\"."
msgstr ""
#: includes/class-m365-login-settings.php:184
msgid "The application (client) ID must be a GUID."
msgstr ""
#: includes/class-m365-login-settings.php:196
msgid "The client secret contains invalid characters."
msgstr ""
#: includes/class-m365-login-settings.php:200
msgid "The client secret could not be encrypted. Is the OpenSSL extension available?"
msgstr ""
#: includes/class-m365-login.php:94
msgid "Settings"
msgstr ""
#: includes/class-m365-login.php:105
msgid "M365 Login requires PHP 7.4 or newer."
msgstr ""
#: includes/class-m365-login.php:106 includes/class-m365-login.php:115
msgid "Plugin activation failed"
msgstr ""
#: includes/class-m365-login.php:114
msgid "M365 Login requires the PHP OpenSSL extension (needed to verify Microsoft token signatures and to encrypt the client secret)."
msgstr ""