=== Open Access SSO ===
Contributors: idgold
Donate link: https://ko-fi.com/idgold
Tags: sso, saml, single-sign-on, access-control, authentication
Requires at least: 6.0
Tested up to: 7.0
Stable tag: 2.1.2
Requires PHP: 8.1
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Open-source SAML 2.0 SSO and access control. Map identity-provider attributes to WordPress roles. Multi-IdP, GPL-licensed.

== Description ==

Open Access SSO is a SAML 2.0 Service Provider for WordPress. It lets users sign in to your site through any standard SAML identity provider (Microsoft Entra ID, Okta, OneLogin, Keycloak, ADFS, Shibboleth, NetIQ Access Manager, etc.) instead of (or alongside) the built-in WordPress login form.

Built from scratch as a clean-room implementation. Fully open-source under GPLv2+. No paid tier, no telemetry, no external dependencies beyond xmlseclibs (MIT) for XML signature handling.

= What it does =

* **SAML 2.0 SP** with HTTP-Redirect and HTTP-POST bindings, signed AuthnRequests, signed/encrypted assertion handling, SP-initiated and IdP-initiated Single Logout.
* **Multi-IdP** — configure multiple identity providers, let users choose via a button or URL parameter (`?idp=slug`).
* **Attribute mapping** — map SAML attributes to WordPress user fields (first name, last name, email, display name, plus arbitrary user_meta).
* **Role mapping** — assign WordPress roles based on SAML attribute values, with exact / contains / regex match types, per-IdP rule sets, deny-unmapped option, default-role fallback.
* **Page access control** — restrict pages, posts, and custom post types to specific roles or to "logged in via SSO" users; per-page meta box; `[oasso_restrict]` shortcode.
* **WooCommerce integration** (optional) — map SAML attributes to WooCommerce customer fields; auto-link SSO users to existing customers.
* **Audit log** with configurable retention.
* **Force-SSO mode** with emergency bypass via `OASSO_BYPASS` constant in `wp-config.php`.

= Privacy =

This plugin's only outbound HTTP requests go to the IdP metadata URL you enter: once when an administrator clicks "Fetch IdP Metadata from URL", and — only if you turn on the optional certificate-rotation check for an IdP — on a recurring WP-Cron schedule that re-fetches that same URL. Certificate-rotation checks are disabled by default. No telemetry, no analytics, no calls to any third-party service. All configuration is stored in `wp_options` on your own site.

= Requirements =

* WordPress 6.0+
* PHP 8.1+
* A SAML 2.0 identity provider you control or have access to.

== External Services ==

This plugin is a SAML 2.0 Service Provider (SP). It sends no telemetry or analytics and never connects to any service operated by the plugin author. Its only external interactions are with the SAML Identity Provider (IdP) that you, the site administrator, configure — for example Microsoft Entra ID, Okta, OneLogin, Keycloak, ADFS, Shibboleth, or NetIQ Access Manager. There is no built-in or default IdP; the IdP is chosen and operated by you or your organisation.

= Identity Provider metadata fetch =

When an administrator clicks "Fetch IdP Metadata from URL" in the plugin's admin screens, the plugin makes a single server-side HTTP GET request to the metadata URL the administrator entered. No site or user data is sent beyond a standard HTTP request; the response (SAML metadata XML) is parsed and stored in your site's database. This never happens on the front end.

Optionally, you can enable a certificate-rotation check for an IdP (off by default). When enabled, WP-Cron re-fetches that same administrator-entered metadata URL on a schedule (for example daily) so the plugin can warn you before the IdP's signing certificate expires or changes. This is the only automatic outbound request the plugin makes, it is opt-in per IdP, and it contacts only the metadata URL you configured.

= SAML single sign-on flow =

When a visitor signs in through SSO, their browser is redirected to your configured IdP (carrying a standard SAML AuthnRequest). After the visitor authenticates, the IdP returns a signed SAML assertion to your site, which the plugin validates and uses to create or update the corresponding WordPress user. The data exchanged is the SAML authentication request and response — which includes the user identifier and whatever attributes your IdP is configured to release. This exchange happens only when a visitor initiates an SSO login.

Because the IdP is a service you select and operate (or that your organisation operates), its terms of service and privacy policy are defined by that provider. Consult your chosen identity provider's own documentation for those terms (for example, the privacy and terms pages of Microsoft Entra ID, Okta, OneLogin, etc.).

== Installation ==

1. Download the .zip and upload via Plugins → Add New → Upload Plugin, or extract into `/wp-content/plugins/open-access-sso/`.
2. Activate through the Plugins menu.
3. Navigate to **Tools → Open Access SSO** to configure.
4. Add an identity provider: upload the IdP metadata XML, paste a metadata URL, or enter the IdP fields manually.
5. Copy the SP metadata URL shown on the dashboard and register your WordPress site as a Service Provider in your IdP.
6. Test sign-on using the SSO button on the login page.

== Frequently Asked Questions ==

= Does this plugin require a paid license? =

No. There is no paid tier. All features are in the GPLv2+ codebase.

= Does it track my users or call home? =

No. The plugin makes no outbound network requests except to the IdP metadata URL you configure: when an administrator initiates a metadata fetch, and — only if you enable the optional, per-IdP certificate-rotation check (off by default) — on a recurring WP-Cron schedule to that same URL. It never contacts the plugin author or any third party.

= How do I enable emergency admin access if SSO breaks? =

Add `define( 'OASSO_BYPASS', 'a-strong-random-secret' );` to `wp-config.php`, then visit `/wp-admin/?oasso_bypass_key=a-strong-random-secret`. The bypass key must be at least 24 characters; per-IP rate limiting prevents brute force.

= Can I use multiple identity providers? =

Yes. Configure each IdP under Tools → Open Access SSO → Identity Providers. Users pick via a button on the login page or via a `?idp=slug` URL parameter.

= Is this related to the OpenSSO product from Sun / Oracle? =

No. Open Access SSO is an independent open-source project, not affiliated with Sun Microsystems' or Oracle's discontinued OpenSSO product.

== Screenshots ==

1. Open Access SSO dashboard - SSO status, configured Identity Providers, and module toggles.
2. Identity Provider configuration - SAML endpoints, signing certificate, and attribute mapping.
3. Role mapping rules - assign WordPress roles from SAML attribute values.
4. Per-page access control in the editor - restrict by role or require SSO login.

== Changelog ==

= 2.1.2 =
* Security: SSO no longer assigns administrator-level roles (any role carrying capabilities such as `manage_options` or `edit_users`) unless you explicitly enable the new "Allow Administrator-Level Roles via SSO" setting in General Settings (off by default). This prevents a role-mapping rule from silently elevating an auto-provisioned SSO user to a role that can take over the site. Users who would have received such a role get the default role instead. If you deliberately map an IdP identity or group to an administrator-level role, enable this setting; the configuration importer enables it automatically when an imported config already maps to such a role.
* Fix: term (category/tag/custom taxonomy) restriction fields now authorise against each taxonomy's own editing capability via the `edit_term` meta capability, instead of a hardcoded `manage_categories`. Custom taxonomies that use their own capabilities now save restriction settings correctly.

= 2.1.1 =
* Security hardening (SAML signature handling): the IdP's signature algorithm is now read before XML-DSig reference processing and checked against an explicit allowlist of RSA algorithms (RSA-SHA256/384/512; RSA-SHA1 only when the SHA-1 fallback is enabled). This makes the protection against signature-algorithm confusion explicit and robust, and lets IdPs that sign with RSA-SHA384/512 verify (previously only RSA-SHA256 was accepted). The same SHA-1 opt-in now also gates Redirect-binding signatures.
* Security hardening: the assertion-level `Issuer` is now required and must match the configured IdP, and assertions must carry an `AudienceRestriction` naming this Service Provider. A new "Require audience restriction" Service Provider setting (on by default) lets you relax the audience check for an IdP that legitimately omits it.
* Fix: the Service Provider screen showed outdated `?ossa=acs` / `?ossa=slo` URLs; it now shows the correct `?oasso_acs=1` / `?oasso_slo=1` endpoints. Removed a non-functional metadata "Download" button (copy the metadata from the field shown instead).
* Maintenance: uninstall now also removes term-level restriction settings; the optional certificate-rotation cron is documented in the External Services section; assorted internal identifier and PHP-requirement cleanups.

= 2.1.0 =
* WordPress.org review compliance: the plugin's internal prefix was renamed from the 3-character `oas_` / `OAS_` to `oasso_` / `OASSO_` across all options, hooks, transients, cron events, user/post meta, AJAX actions, nonces, asset handles, and custom tables. The PHP namespace (`OpenAccessSSO`) and plugin slug (`open-access-sso`) are unchanged.
* The content-restriction shortcodes are renamed `[oas_restrict]` → `[oasso_restrict]` and `[oas_login_button]` → `[oasso_login_button]`.
* Security: the `[oasso_restrict]` shortcode now passes its returned content through `wp_kses_post()`.
* Security: the admin "Test Connection" link now carries and verifies a nonce.
* **Because the internal prefix changed, this is NOT a drop-in upgrade. Export your configuration first, then reinstall and re-import — see the Upgrade Notice below for the exact steps.**

= 2.0.4 =
* Maintenance release addressing WordPress.org plugin review feedback. All inline scripts and styles are now delivered through the WordPress enqueue APIs (`wp_add_inline_script`, `wp_add_inline_style`, `wp_get_inline_script_tag`) or a linked stylesheet, instead of raw `<script>`/`<style>` tags.
* Removed obsolete pre-PHP-8.0 `libxml_disable_entity_loader()` calls. The plugin requires PHP 8.1+, where libxml ≥ 2.9 already disables external-entity loading by default and `LIBXML_NONET` blocks network access; the calls were dead code and deprecated in PHP 8.0.
* Documented the SAML Identity Provider external-service interaction in a dedicated readme section.
* No functional or behavioural change.

= 2.0.3 =
* Added an optional "support development" link (Ko-fi) in the admin footer of the plugin's own pages, plus a Donate link in the readme. No functional change; the link opens externally and the plugin makes no network calls of its own.
* Compatibility: declared tested up to WordPress 7.0.

= 2.0.2 =
* PCP polish: WordPress Plugin Check now reports zero findings for the distributable zip. Two real sanitiser additions in the admin settings form; the rest are inline `phpcs:ignore` annotations with reason comments at intentional sites (cross-origin POST at the SAML ACS endpoint, PCRE limit hardening before user-supplied regex evaluation, table DROP on uninstall, internal-only DB query composition). No behaviour change.
* Build: `README.md` (GitHub-only readme) is no longer shipped in the distributable zip; `composer.json` is now included so the bundled `vendor/` directory is transparent to plugin reviewers.

= 2.0.1 =
* Fix: Test Connection now populates results and attribute dropdowns reliably regardless of how long the admin has been logged in. The admin's identity is now recorded at initiate-time in a server-side transient keyed by the AuthnRequest ID, and looked up at the ACS callback via the response's InResponseTo. The previous flow depended on the WP auth cookie surviving the cross-origin POST from the IdP, which modern browsers block under SameSite=Lax outside a brief carveout window.

= 2.0.0 =
* Initial release on WordPress.org. (Project formerly known internally as OpenSSO Access; renamed to Open Access SSO ahead of public release.)
* Full SAML 2.0 SP with multi-IdP support.
* Attribute mapping, role mapping, page access control, WooCommerce integration.
* Audit log, force-SSO mode, emergency bypass via `OAS_BYPASS`.

== Upgrade Notice ==

= 2.1.2 =
Security: SSO no longer grants administrator-level roles unless you enable "Allow Administrator-Level Roles via SSO" (General Settings, off by default). If you map an IdP identity or group to an admin role, enable it after upgrading. Re-importing such a config enables it automatically.

= 2.1.1 =
SAML signature, Issuer and Audience validation hardened. If SSO fails after upgrading because your IdP omits the AudienceRestriction, disable "Require audience restriction" in Service Provider settings. Upgrading from 2.0.x still needs the export/reinstall/import steps below.

= 2.1.0 =
Not a drop-in upgrade (internal prefix changed). Export your config (Tools > Import/Export), delete the old version, install 2.1.0, then re-import. Also rename OAS_BYPASS to OASSO_BYPASS in wp-config and point your IdP ACS/SLO URLs at ?oasso_acs / ?oasso_slo. See changelog.

= 2.0.4 =
WordPress.org review compliance and maintenance. No behaviour change; safe drop-in upgrade.

= 2.0.3 =
Adds an optional Ko-fi support link in the admin footer. No behaviour change.

= 2.0.2 =
Plugin Check polish; no behaviour change. Safe drop-in upgrade.

= 2.0.1 =
Fixes Test Connection silently failing for admins whose login is more than ~2 minutes old. Drop-in upgrade; no settings change required.

= 2.0.0 =
First public release.
