WP Manifestindependent plugin directory
manifest / security / wp-simple-saml

WP Simple SAML WP Registry grade D+. High-severity findings · 2 findingsWP RegistryHigh-severity findings · 2 findingsOpen the reportD+ #3 in Security

WordPress Simple SAML plugin

★ 125stars
333kcomposer installs
0.5.5latest · 9mo ago

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/humanmade/wp-simple-saml/archive/refs/heads/master.zip

On Packagist as humanmade/wp-simple-saml:

composer require humanmade/wp-simple-saml
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.
WordPress Simple SAML
Easy to use Single-sign-on ( SSO ) SAML integration plugin for WordPress, with multi-site / multi-network support.
Build status
A Human Made project. Maintained by Shady Sharaf.

WordPress Simple SAML is a flexible, extensible SAML integration plugin, which does most of the grunt work while keeping everything configurable through actions and filters throughout the plugin.

The plugin supports multi-site networks, and cross-network SSO delegation as well. Can be installed on site-level, or on network-level.

WordPress Simple SAML uses OneLogin's PHP-SAML toolkit for SAML API integration.

Getting Set Up

  • Copy the plugin files to your wp-content/plugins directory
  • Activate the plugin
  • Head over to configuration screen.
  • Send Service Provider metadata URL ( or content ) to your Identity Provider authority (IdP) ( find the link in settings page ).

Note: If you're activating the plugin network-wide via code, you might need to use the filter wpsimplesaml_network_activated to override the standard WordPress check, something like this would be what you need:

add_filter( 'wpsimplesaml_network_activated', '__return_true' )

Configuration

There is two ways to configure the plugin, either from the admin interface or using filters, both can be used interchangably as the project requires, usually filters prevail database settings though.

Admin configurations

Depending on whether the plugin is network-activated or not, you will need to go to Settings \ General or Network Settings pages.

  • SSO Status is how the plugin integrates with WordPress login process, available options are Disable, Display login link which only provides a link in the login form, Force redirect which overrides the login form altogether and directly goes to SSO login page.
  • SSO Base URL (optional) is the home URL of the WordPress site that serves as the delegate ( main service provider ) to which SAML responses will be posted, usually this is the main site of the network, and is the same value for siteurl option, eg https://my.site/
  • SSO IdP Metadata (required, if not filtered) Copy of the SSO IdP metadata XML file, which can also be passed via either wpsimplesaml_idp_metadata_xml_path for a path to the XML file, or wpsimplesaml_idp_metadata_xml for the contents of the XML, or wpsimplesaml_idp_metadata for the configuration array.
  • SSO delegation whitelisted hosts List of hosts to whitelist during delegation of SAML responses, ie: secondary domains that needs to use SSO as well from the same IdP. Local sites are allowed by default.
  • SSO Role Management Enables developers to assign different roles to users based on SAML Responses, disabled by default, and is controlled via a few filters,
  • SSO Debug via Cookies Allows developers to use a special cookie named sso_debug to override the SSO Status option during testing. Possible value of the cookie are force and link, which are self-explanatory.
  • SSO Config validation Shows information about IdP metadata and validity of its settings.

Configuration via code

WordPress Simple SAML is built to be as extensible as possible, so most aspects of the login/validation process can be tweaked as needed by using the available well-documented Hooks.

TL;DR; This is the basic minimum filters you'd need to get an implementation working with the default options.

// SAML metadata XML file path
add_filter( 'wpsimplesaml_idp_metadata_xml_path', function(){
    return ABSPATH . '/.private/sso/test.idp.xml';
} );

// Configure attribute mapping between WordPress and SSO IdP SAML attributes
add_filter( 'wpsimplesaml_attribute_mapping', function(){
    return [
        'user_login' => 'uid',
        'user_email' => 'email',
    ];
} );

Testing

This plugin can be tested or developed within any local WordPress environment. For convenience, a local WordPress server can also be run by executing npm run dev: this command will start a local server at http://localhost:8888/ (requires Docker). Run npm run wp-env stop to shut down the server when finished.

As SSO authorities are usually a pain to change, and credentials take time to be configured, we've documented steps to get a sample SAML IdP ( Identity provider ) up and running using a test docker container. See more at Testing SSO Locally.

Contribute

TL;DR

# Make sure you have Docker installed, then run:
npm install
npm run dev

First, thanks for contributing!

Setting up

To get development dependencies, install composer and npm dependencies via:

composer install
npm install

This installs Human made's version of WordPress Coding Standards via PHP Code Sniffer, and symlinks a precommit hook to automatically check all commits for code quality concerns.

Workflow

  • Find an issue you'd like to help with, or create a new one for the change you'd like to introduce.
  • Fork the repo to your own account
  • Issue pull-requests from your fork to ours
  • Tag the issue you're trying to resolve in your pull-request for some context
  • Make sure the pull-request passed all Travis checks
  • Tag any of the contributors for a review.

Next

Check issues list for what's planned next.

Credits

Created by Human Made for network-wide SAML SSO Integrations, because of the lack of a well-written WordPress integration with the features/flexibility our clients require.

Written and maintained by Shady Sharaf. Thanks to all our contributors.

Interested in joining in on the fun? Join us, and become human!

Changelog

  • 0.3
    • Fix compatibilty with WordPress single site (no multisite).
    • Fix error caused by using WordPress dashboard function outside the dashboard.
  • 0.2.1
    • Fix missing composer dependency and updated .gitignore
  • 0.2
    • Updating PHP SAML library to 3.0 to support PHP 7.x
  • 0.1
    • Stable version

Credits

Created by Human Made

Thanks to all our contributors.

Interested in joining in on the fun? Join us, and become human!

Releases

9 releases.

Tag
Published
0.5.5 latest
Jan 15, 2026 9mo ago
Dec 11, 2025 10mo ago
Apr 29, 2025 1 year ago
Apr 29, 2025 1 year ago
Apr 8, 2024 2 years ago
May 23, 2023 3 years ago
Oct 26, 2021 4 years ago
Sep 29, 2021 5 years ago
Nov 8, 2019 6 years ago

These releases are tags only. The author does not attach a packaged zip, so there are no download counts to report.

D+ grade

Security

WP Simple SAML 0.5.5 · audited by WP Registry

High-severity findings.

2 high
Audited release
0.5.5
Findings
2
Worst severity
high
Content hash
1e20521499e877c3f9489890…

Findings

  • high SAML ACS runs php-saml with strict=false, allowing replay of signed assertions without timestamp/audience/destination checks

    auth_bypass

    Admin\get_config() builds the OneLogin settings array with only sp.entityId / ACS / SLS / NameIDFormat on top of parsed IdP metadata. It never sets strict, security.wantAssertionsSigned, or security.wantMessagesSigned. OneLogin\Saml2\Settings in the bundled php-saml 3.8.1 defaults _strict=false; isValid() then wraps Destination, Audience, Issuer, Conditions/NotOnOrAfter, SubjectConfirmation, XML schema, and the want*Signed flags inside if ($this->_settings->isStrict()). Signature verification still runs outside that block (unsigned responses are rejected; CVE-2025-66475 is patched here via xmlseclibs 3.1.4), so this is not a from-scratch Golden SAML forge. It is an authentication bypass given any previously valid signed assertion from the configured IdP: (a) expired assertions replay forever; (b) an assertion issued for a different SP that shares this IdP cert (another app, another WordPress site) is accepted because audience/destination are not checked; (c) process_response() calls $saml->processResponse() with no AuthNRequest id, so InResponseTo is never bound. action_verify is unauthenticated POST /sso/verify. On success get_or_create_wp_user() matches get_user_by('email') (administrators included) or wp_insert_user, then signon() sets an auth cookie with remember-me. php-saml's own README states strict MUST be true in production. A site owner can set it via the wpsimplesaml_config filter; the admin UI and default config never do.

    inc/admin/namespace.php:81-92

    Recommendation

    Always pass 'strict' => true in the array returned from get_config() (and do not disable it on the web ACS; the WP-CLI response command already opts out on purpose). Set security.wantAssertionsSigned (and ideally wantMessagesSigned) to true, keep an IdP x509cert (not fingerprint-only), and persist the AuthNRequest id from Auth::login() so processResponse($requestId) can reject unsolicited/replayed responses outside the assertion NotOnOrAfter window. Reject assertions whose Audience/Destination do not match this SP.

  • high Multisite cross-site SSO auto-trusts any *.local host and forwards the SAMLResponse before validation

    auth_bypass

    cross_site_sso() is hooked to wpsimplesaml_action_login and wpsimplesaml_action_verify at priority 10, before action_verify validates the assertion. If $_POST['SAMLResponse'] is set and get_blog_id(get_redirection_url()) is not the current blog (true for any off-network URL on multisite; get_blog_id short-circuits to the current blog on single-site), it calls cross_site_sso_redirect(). That function treats every hostname ending in '.local' as allowed, ignoring the empty default sso_whitelisted_hosts list. For redirect_to/RelayState with path '/' or containing '/wp-admin', it then prints an auto-submit form (assets/csr.js) whose action is https://{that-host}/sso/verify and whose body is the raw SAMLResponse. get_redirection_url() takes $_REQUEST['redirect_to'] or $_POST['RelayState'] with only FILTER_VALIDATE_URL (no wp_validate_redirect). Attack: send a victim to /sso/login?redirect_to=https://evil.local/ (or https://evil.local/wp-admin/). After they authenticate at the real IdP, the browser POSTs the bearer assertion to the attacker. Combined with strict=false, that stolen response can be replayed at the real ACS with no expiry. Requires multisite and that the victim resolver will answer *.local (mDNS/LAN or an internal zone); the allowlist is hardcoded, not an admin opt-in.

    inc/namespace.php:638-680

    Recommendation

    Do not auto-allow *.local in production. Validate the assertion on the receiving SP before forwarding, and only forward to hosts in sso_whitelisted_hosts (plus hosts that get_blog_id() maps to a real site on this network). Derive the form action from get_site_url($blog_id) rather than the attacker-supplied URL. Run redirect_to/RelayState through wp_validate_redirect() and never POST SAMLResponse to a host that is not on that allowlist.

WP Registry hashes the installable build and reports on that exact bytes-for-bytes copy. Embargoed findings are withheld until they are disclosed, so a clean verdict means nothing public is outstanding. WP Manifest does not audit code itself.