Authenticate users into WordPress with the iMIS EMS Enterprise built-in OAuth SSO. The plugin signs members into WordPress using their iMIS credentials, synchronizes their profile data and roles from iMIS on every login, and provides role-based security for pages, posts, and media library documents.
The plugin uses iMIS as the identity provider. iMIS must be the login page.
$/Philstack/UsernameData IQA query (and, optionally, committee data from $/Philstack/UsernameDataCommittees).php-curl PHP extension must be installed, enabled, and up to date (verify with the [ps-sso-curlcheck] shortcode).https://example.org/?ps_sso_endpoint=return (pretty alias: https://example.org/ps-sso-return/). Copy it from the iMIS URL and Client Application Settings panel and configure it in iMIS as the SSO redirection target.https://example.org/?ps_sso_endpoint=login (pretty alias: https://example.org/ps-sso-login/). Used automatically; original-page tracking works with nothing configured. Leave Login Redirector URL blank.Sites set up on earlier versions with [ps-sso-return] and [ps-sso-login-redirector] pages keep working unchanged. To retire them: point iMIS at the SSO Return URL above, clear the Login Redirector URL setting, then delete the two pages.
Step-by-step setup instructions with screenshots are available on the Philstack website.
The plugin adds a top-level Philstack's iMIS SSO menu to the WordPress admin (requires the manage_options capability, i.e. administrators).
Admin → Philstack's iMIS SSO
The main plugin page. It shows:
Admin → Philstack's iMIS SSO → Secured Content
A list table of every post, page, custom post type item, and media document that has role-based security assigned (anything with security roles saved on it).
Use this screen to audit exactly which content on the site is restricted and to whom.
Admin → Philstack's iMIS SSO → Role Manager
Create, rename, and delete WordPress roles without a separate role-editor plugin.
Roles created here (or automatically by the SSO role sync) can be used in the page and document security role pickers.
Admin → Philstack's iMIS SSO → Documentation
Opens this documentation (the flat-file HTML version bundled with the plugin) in a new browser tab, so the full reference is always available for the exact plugin version installed.
All settings live on the main Philstack's iMIS SSO page and are stored together in one WordPress option (ps_imis_sso_plugin_options).
| Setting | Description |
|---|---|
| License Key | The license key provided by Philstack. All features are enabled once a valid license is entered. See License Validation for caching/grace-period behavior. |
| Setting | Description |
|---|---|
| iMIS URL | The base URL of your iMIS instance. Include the https:// prefix and add nothing after the domain (example: https://acme.imiscloud.com). |
| Client ID | The client application ID configured in iMIS for this integration. |
| Shared Secret | The client application shared secret from iMIS. Stored in the WordPress options database. |
| SSO Return URL | Read-only. The plugin's built-in SSO return endpoint (/?ps_sso_endpoint=return, with the pretty alias /ps-sso-return/ also registered). Configure this URL in iMIS as the SSO redirection target. No WordPress page and no [ps-sso-return] shortcode is required. |
These control redirection behavior before, during, and after the SSO.
| Setting | Description |
|---|---|
| Login URL | Full URL of the iMIS SSO redirector page (the iMIS-hosted login entry point). |
| Login button label | Label for the login state of [ps-sso-auth-button] and the nav-menu Login item. Default: Login. |
| Login Redirector URL | Optional; leave blank. Original-requested-page tracking now runs on the plugin's built-in login endpoint, so users land back where they started with nothing configured. Set this only if you are still using a page containing the legacy [ps-sso-login-redirector] shortcode. The panel shows which URL is currently in use. |
| Fallback Return URL | Full URL used after SSO when the originally requested page cannot be determined. |
| Access Denied URL | Full URL where logged-in users land when they lack access to the requested content. If blank, the site homepage is used. |
| Logout URL | Full URL where the browser lands after the user clicks the Sign Out button. |
| Single Logout (iMIS 20.3.228+) | Checkbox. Switches from the legacy redirect logout to the iMIS single-logout (front-channel) model. See Logout & Single Logout. The settings panel also displays the WordPress front-channel endpoint URL to paste into your iMIS SingleLogout content record. |
| iMIS Logout Endpoint URL | Single Logout only. The iMIS endpoint the browser calls to end the iMIS session. Leave blank to derive {iMIS URL}/Authentication/Logout automatically. |
| Logout button label | Label for the logout state of [ps-sso-auth-button] and the nav-menu Logout item. Default: Logout. |
| Profile page URL | Destination for [ps-sso-profile-button]. |
| Profile button label | Label for [ps-sso-profile-button]. Default: Profile. |
| Renewal page URL | Destination for [ps-sso-renewal-button] and [ps-sso-renewal-banner]. |
| Renewal button label | Label for [ps-sso-renewal-button]. Default: Renew Now. |
| Renewal not available message | Text shown by [ps-sso-renewal-button] when the logged-in user is not eligible for renewal. Default: Renewal not available. |
| Renewal banner text | Link text for [ps-sso-renewal-banner]. Default: Renew Your Membership. |
Controls what happens when someone hits secured content, as an alternative to redirecting.
| Setting | Description |
|---|---|
| Display Authorization Required Message instead of Redirect | Checkbox. When checked, not-logged-in visitors to secured content see a message in place of the content instead of being redirected to the login flow. |
| Authorization Required Message | Rich-text (HTML) message shown to not-logged-in visitors when the option above is on. Shortcodes are supported. Default: "Authorization required. Please log in to access this content." |
| Display Access Denied Message instead of Redirect | Checkbox. When checked, logged-in users without a required role see a message in place of the content instead of being redirected to the Access Denied URL. |
| Access Denied Message | Rich-text (HTML) message shown to logged-in users without access when the option above is on. Shortcodes are supported. Default: "You do not have permission to access this content." |
Both behaviors are global defaults — every secured page/post can override them individually in its security meta box. Combine with the [ps-sso-content-gate] shortcode to show a public teaser above the message.
| Setting | Description |
|---|---|
| Enable Secured Documents | Checkbox. Extends role-based security to media library files (PDFs, images, zips, etc.). When enabled, a Philstack Document Security panel appears on every media item. Securing a file moves it into a protected store so its public URL is routed through WordPress for a role check — no server configuration required; works on Apache and Nginx/managed hosts. When disabled, all secured files are moved back to their public locations and served normally again (per-document settings are preserved for instant re-enable). A warning and confirmation appear if you disable the feature while documents are secured. |
| Optional host rewrite rule (WP Engine / static-uploads hosts) | Expandable panel. Most hosts work out of the box. Hosts that serve /wp-content/uploads/ statically and hard-404 missing files (e.g. WP Engine) need one regex redirect rule — shown in this panel with copy-ready Redirect from / Redirect to values — so raw document URLs are routed through WordPress for the access check. On WP Engine add it in the Web Rules Engine using the Internal Rewrite action so the raw URL stays in the address bar. Note: every matching file (including public ones) is then delivered through PHP instead of the CDN. |
| "I have installed the host rewrite rule above" | Checkbox inside the panel above. When checked, secured documents render their original /wp-content/uploads/ URL instead of the /ps-protected-document/ handler URL, since the host rule already routes raw URLs through WordPress. Leave unchecked if you have not added the rule, or links to secured files will 404 on static-uploads hosts. |
Access behavior for a secured document:
| Setting | Description |
|---|---|
| Use the iMIS IQA "sso-role" field as the WordPress role | Checkbox. When checked, each user's main WordPress role is derived from the sso-role code returned by the UsernameData IQA (a matching role is created automatically if needed, with Subscriber capabilities). When unchecked, the Default role below is used. |
| Default role | The WordPress role assigned when the checkbox above is not checked. Default: Subscriber. |
| Role to exempt from the SSO role update process | Users holding this role are skipped by role synchronization — their roles are never added or removed by SSO. WordPress administrators are always automatically exempt. Default: Administrator. Note: exemption protects role assignments only; profile data and custom fields still sync. |
Selects how the plugin matches incoming iMIS users to existing WordPress accounts (and how usernames are generated for new accounts):
| Option | Description |
|---|---|
| Match by Email Address (default) | Matches the iMIS user's email address to a WordPress account. New accounts use the email as the username (falling back to imis_{id} if taken). |
| Match by First Name + Last Name | Concatenates the iMIS first and last name to match the WordPress username; spaces and special characters are removed or replaced (e.g. "John Doe" → johndoe). New usernames get the iMIS ID appended if taken. |
| Match by iMIS Username | Matches the iMIS username field directly to the WordPress username. |
If no match is found, a new WordPress user is created using the selected method. If the iMIS email already belongs to a different WordPress account, the new account is created with a modified, unique email to avoid the conflict (logged in debug mode).
Sync additional iMIS IQA columns (e.g. academic or personalization data) into WordPress user meta on every SSO login — no code and no extra API calls required.
| Setting | Description |
|---|---|
| Enable Custom Field Sync | Checkbox, off by default. When enabled, the fields mapped below are stored as user meta on every SSO login. When disabled (or when a mapping is removed), previously synced values are cleaned up on each user's next login. |
| Field mappings | Textarea, one mapping per line: imis-column-name or imis-column-name => meta_key. Column names must match the IQA output exactly (matching is case-insensitive). If no meta key is given, one is derived from the column name. Lines starting with # are comments. An Active mappings preview table below the field shows exactly which iMIS column maps to which user meta key and the ready-to-copy [ps-sso-user-field] shortcode for each. |
Details:
$/Philstack/UsernameData IQA in iMIS, then list it in the mappings. Values are stored as user meta prefixed with ps_sso_ and can be displayed with the [ps-sso-user-field] shortcode.username, imis-id, first-name, last-name, email, sso-role, sso-role-desc, company, sso-renewal-admin.| Setting | Description |
|---|---|
| Hide WordPress Admin Bar | Checkbox. When enabled, the WordPress admin toolbar is hidden on the front end for all users except administrators. |
| Setting | Description |
|---|---|
| Debug Mode | Checkbox. Enables verbose SSO flow logging (user matching, role assignment, redirects, API responses). The log is displayed by the [ps-sso-welcome] shortcode and stored in the browser session (cleared when the browser closes). Additional detail is written to the PHP error log. |
| Skip cURL cert validation | Checkbox — for local/development testing only. Disables TLS certificate verification on API calls. Never enable in production. |
| Store iMIS Committee data as user meta | Checkbox. When enabled, committee assignments from the $/Philstack/UsernameDataCommittees IQA are fetched on each login, stored in user meta (imis_committee_data), displayed on the user's profile screen, and used to assign iMIScommittee_-prefixed WordPress roles. When disabled, the committee API call is skipped, stored committee meta is cleared, and stale committee roles are removed on the next login. |
Every public post type's edit screen (posts, pages, custom post types) gets a Philstack Role-Based Page Security meta box in the sidebar:
The block editor shows a persistent notice banner stating whether the current page is public or restricted (and to which roles), updating live as you change the checkboxes.
Both custom-message boxes are full rich-text (WYSIWYG) editors with Visual and Text tabs. Under the block editor, WordPress relocates meta boxes in the page after they render, which can leave a rich-text editor visible but impossible to click into or type in; as of 3.12.2 the plugin detects that state and re-initializes the editor automatically, so no action is needed.
Teaser previews: place [ps-sso-content-gate] in the content to show everything above it publicly while gating everything below it (see the Shortcode Reference).
Message display replaces only the protected post's own content. Site furniture rendered by the theme — the sitewide footer, headers, sidebars, widgets, and secondary loops such as related-post lists — is left untouched and continues to display normally on a gated page.
Secured pages appear in the Secured Content admin list.
With Secured Documents enabled, every media item (in both the media modal and the Edit Media screen) gets a Philstack Document Security panel:
If a secured file cannot be moved into the protected store (e.g. the uploads directory is not writable), an admin notice appears and the file may still be publicly accessible until permissions are fixed and the media is re-saved.
Appearance → Menus gains a PS-SSO Authentication box that adds smart Login and Logout items to any menu:
redirect_to set to the current page) — the configured Login Redirector URL if one is set, otherwise the built-in endpoint.[ps-sso-auth-button] (including Single Logout when enabled).On the WordPress user profile screen (Users → Profile / Edit User), the plugin displays:
[ps-sso-return] — deprecatedNo longer required. The SSO token exchange runs on the built-in SSO Return URL endpoint; point iMIS there instead of at a page. The shortcode is kept so existing installs keep working, and a page containing it can be deleted once iMIS is repointed.
When it does run, the plugin exchanges the token, fetches the user's iMIS data, logs them into WordPress, and forwards them to their original page (or the Fallback Return URL). Renders nothing on a normal visit; requires a valid license.
Execution point. Logging a user in sets an authentication cookie, and both that cookie and the final redirect are HTTP headers — so they can only be sent before the page produces output. The built-in endpoint runs on parse_request, before WordPress has even resolved a post. The legacy page flow runs on template_redirect, before the theme emits anything, with the shortcode itself as a last-resort fallback.
For the legacy page flow this matters because the page is detected by looking for the shortcode in the post content, not by waiting for it to render. A theme or page builder that never calls the_content() would otherwise skip the login entirely. Either way, a successful SSO return always responds with a 302 redirect and never a 200 HTML page.
Troubleshooting: if a POST to the SSO return page responds
200 OKwith a rendered page instead of a302, the login did not complete. On plugin versions before 3.12.1 this was the expected symptom when PHP output buffering was disabled — the<head>had already been sent, sowp_set_auth_cookie()andwp_redirect()both failed silently. Enable Debug Mode and check the log forHeaders already sent: YES.
Trailing slashes. Configure the iMIS SSO return URL to match the WordPress permalink exactly, including a trailing slash if your permalink structure uses one (https://example.org/sso-return/). WordPress skips canonical redirects on POST requests, so a mismatch usually works — but any redirect placed in front of a POST discards the request body and the refresh token along with it.
[ps-sso-login-redirector] — deprecatedNo longer required. Original-page tracking runs on the plugin's built-in login endpoint and is on by default; leave Login Redirector URL blank to use it. The shortcode is kept so existing installs keep working, and the page containing it can be deleted once the setting is cleared.
The behavior is identical either way: when a visitor arrives with a redirect_to query parameter, the redirector stores the originally requested URL (redundantly, via cookie, PHP session, and database transients, to survive restrictive hosting/CDN configurations) and forwards the visitor to the iMIS login page. After SSO completes, the visitor is returned to the stored URL. Already-logged-in visitors are sent straight back.
For security, redirect_to values pointing off-site are rejected and ignored — visitors are only ever returned to a URL on this site.
Page caching. The redirector is a pure side-effect page: it must execute PHP on every visit to work at all. If a full-page cache stores its response, PHP stops running and the login link silently does nothing. The plugin defends against this automatically:
parse_request (built-in endpoint) or template_redirect (legacy page), before the theme produces any output, so the 302 and its cache headers are always sent cleanly.DONOTCACHEPAGE, Cache-Control: no-store, Pragma: no-cache, and X-Accel-Expires: 0 (read by the Nginx page caches used by managed WordPress hosts such as Kinsta and WP Engine).[ps-sso-auth-button], the nav-menu login item, and role-based page security carry a per-request ps_nc value so a repeated URL cannot be served from cache.post_content — or if the shortcode is accidentally removed from the page.Editor safety. Both deprecated shortcodes have side effects — they set cookies, redirect, and exit. They now run only on live front-end page views: they are inert during admin, REST, AJAX, cron, feed, preview, and Elementor editor/preview requests, showing administrators a short placeholder instead. Rendering one during a REST save is what previously made publishing a page containing either shortcode fail with "Updating failed. The response is not a valid JSON response." — is_admin() returns false during a REST request, so the old guard never caught it.
If the login redirector stops working on a cached production site, enable Debug Mode and check the SSO log for Login redirector initialized. If that entry is absent after a failed login attempt, the request never reached PHP and the response is being served from a cache layer that is ignoring the headers above; exclude the redirector and SSO return URLs from the host's page cache.
[ps-sso-auth-button]Displays a Login button for anonymous visitors or a Logout button for logged-in users. Labels and destinations come from the Redirection Settings. The login button routes through the login redirector (carrying the current page as redirect_to) — the configured Login Redirector URL if one is set, otherwise the built-in endpoint. The logout button triggers the AJAX logout flow described in Logout & Single Logout. Buttons use the theme's standard wp-block-button__link wp-element-button classes.
[ps-sso-profile-button]Displays a button linking to the Profile page URL — only when the user is logged in. Renders nothing for anonymous visitors.
[ps-sso-renewal-button]For logged-in users: if the user's iMIS data flags them as renewal-eligible (sso-renewal-admin = true from the UsernameData IQA), displays a button to the Renewal page URL; otherwise displays the Renewal not available message. Renders nothing for anonymous visitors.
[ps-sso-renewal-banner]Like the renewal button, but renders a banner (div.ps-renewal-banner) containing a link with the Renewal banner text. Shown only to logged-in, renewal-eligible users; renders nothing otherwise.
[ps-sso-name-display]Displays Welcome, First Last for logged-in users (using the WordPress first/last name synced from iMIS). Renders nothing for anonymous visitors.
[ps-sso-user-field]Displays a custom iMIS field synced to the logged-in user's meta by Custom iMIS Field Sync.
[ps-sso-user-field field="grad_year" label="Graduation year: " default="—"]
| Attribute | Description |
|---|---|
field | Required. The user meta key without the ps_sso_ prefix — shown in the Active mappings preview in settings. The built-ins imis_id (the user's iMIS ID) and renewal_admin (the renewal-eligibility flag) are also available. |
label | Optional text rendered before the value. |
default | Optional text shown when the user is logged out or the field is empty/unavailable. |
For security, only fields produced by Custom Field Sync (the configured map or the ps_sso_custom_user_fields filter) plus the two built-ins can be displayed — the shortcode cannot read arbitrary user meta.
[ps-sso-welcome]A testing and troubleshooting aid. Displays the auth button plus the logged-in user's WordPress account details (username, roles, name, email, registration date). When Debug Mode is on, it also shows the cURL status, key configuration values, and the full log of the last SSO run. Requires a valid license.
[ps_sso_nested_links]Displays a bulleted list of the current page's child pages, filtered by role-based access: only children the current visitor is allowed to see (unsecured, or secured with one of the visitor's roles) are listed.
[ps-sso-content-gate]A gating marker for teaser previews. Place it inside any post or page that has role-based security assigned: everything before the marker is shown publicly as a teaser; everything after it is hidden and replaced with the authorization-required / access-denied message for visitors who lack access. If the shortcode is not present on a secured page, the entire content is gated. (Applies when the "show message" behavior is active, globally or per-page.)
The gate only affects the protected post's content. The theme's template output — sitewide footer, header, sidebars, and widgets — renders normally around the teaser and message.
[ps-sso-curlcheck]Diagnostic: prints whether the required cURL PHP extension is installed and available.
The logout button ([ps-sso-auth-button], the nav-menu Logout item, or any element with the #ps-imis-sso-logout-button ID) triggers an AJAX call that ends the WordPress session, then redirects. Two modes, controlled by the Single Logout setting:
/Authentication/Logout (the old Logout.aspx no longer applies).{iMIS URL}/Authentication/Logout) to end the iMIS session, and finally lands on the Logout URL.iMIS-initiated logout: when Single Logout is enabled, the plugin exposes a front-channel endpoint at {your-site}/?ps_sso_logout=1. Point the logout_target_url of your iMIS SingleLogout content record at this URL (the exact URL is displayed in the settings panel) so logging out of iMIS also ends the WordPress session. The endpoint logs the user out and redirects to the Logout URL, restricted to same-site destinations.
Cross-site cookie caveat: the WordPress→iMIS logout call only clears the iMIS session if the browser sends the iMIS cookie cross-site; third-party-cookie policies can block this. The most robust configuration is to point the Logout URL at the iMIS SingleLogout content record, letting the iMIS page end its own session (same-origin) and bounce back to the WordPress front-channel endpoint.
On every SSO login the plugin recalculates the user's WordPress roles from current iMIS data:
sso-role code (when Use the iMIS IQA "sso-role" field is checked) or the configured Default role. Missing roles are created automatically with Subscriber capabilities, using the sso-role-desc as the display name.iMIScommittee_ (e.g. iMIScommittee_finance-committee).ILMA Member matches an existing ilmamember).The plugin expects two IQA queries in iMIS (samples are shipped in the plugin's iMIS-iqa/ folder):
$/Philstack/UsernameData — returns the user's core properties:
| Column | Used for |
|---|---|
username | iMIS username; committee lookups; iMIS Username matching mode |
imis-id | Stored as imis_id user meta; uniqueness fallback for generated usernames |
first-name, last-name | WordPress profile name; Full Name matching mode |
email | WordPress email; Email matching mode |
sso-role | Main WordPress role code (when member-type-as-role is enabled) |
sso-role-desc | Role display name; recorded in the user description |
company | Available to the login flow |
sso-renewal-admin | Renewal eligibility flag (true shows renewal button/banner) |
Any additional columns you add to this IQA can be synced with Custom iMIS Field Sync — no code required.
$/Philstack/UsernameDataCommittees — returns committee assignments with properties CommitteeName, CommitteeProductCode, Position, TermStart, TermEnd. Only queried when Store iMIS Committee data as user meta is enabled.
Both hooks run on every SSO login, even when the settings-based Custom Field Sync is disabled.
ps_sso_custom_user_fieldsAdd, alter, or remove the custom fields stored as user meta during SSO login. Runs after the settings-based field map is applied, so you receive its output and can build computed/derived fields from any IQA column.
/**
* @param array $custom_fields Unprefixed meta key => value. The ps_sso_ prefix
* is added on write; keys are sanitized to [a-z0-9_].
* @param array $imis_data Every column returned by the UsernameData IQA
* (Name => Value), mapped or not.
* @param int $user_id The WordPress user ID being synced.
* @return array
*/
add_filter('ps_sso_custom_user_fields', function ($custom_fields, $imis_data, $user_id) {
// Derive a display badge from two raw IQA columns.
if (!empty($imis_data['degree-program']) && !empty($imis_data['graduation-year'])) {
$custom_fields['program_badge'] = $imis_data['degree-program'] . ' ’' . substr($imis_data['graduation-year'], -2);
}
return $custom_fields;
}, 10, 3);
Fields returned by the filter participate in the same lifecycle as mapped fields: they are written with the ps_sso_ prefix, tracked in the per-user index, displayable via [ps-sso-user-field], and cleaned up automatically when no longer returned.
ps_sso_after_user_syncFires after an SSO login has fully synced the user (profile, roles, and meta) — on both the normal path and the exempt-role path. The API instance still holds a valid iMIS access token, so you can run additional IQA queries without re-authenticating.
/**
* @param int $user_id The WordPress user ID.
* @param array $imis_data Every column returned by the UsernameData IQA.
* @param PsSsoApi $api_instance The authenticated API instance (access token
* still valid for follow-up IQA queries).
*/
add_action('ps_sso_after_user_sync', function ($user_id, $imis_data, $api_instance) {
// Example: record the member's company in a CRM sync queue.
if (!empty($imis_data['company'])) {
do_action('my_crm_queue_sync', $user_id, $imis_data['company']);
}
}, 10, 3);
Typical uses: pushing data to other plugins or CRMs, running extra IQA queries, granting WooCommerce memberships, and custom audit logging.
ps_sso_auth_button_htmlCustomize the HTML markup rendered by the [ps-sso-auth-button] shortcode (the combined login/logout button). Runs every time the shortcode is rendered, whether the visitor is logged in or not.
/**
* @param string $html The generated button HTML (login markup when
* logged out, logout markup when logged in).
* @param bool $is_logged_in Whether the current user is logged in.
* @param string $login_url The resolved login URL used when logged out
* (already includes any configured redirect_to).
* @param string $login_button_text Configured login button label.
* @param string $logout_button_text Configured logout button label.
* @return string
*/
add_filter('ps_sso_auth_button_html', function ($html, $is_logged_in, $login_url, $login_button_text, $logout_button_text) {
if (!$is_logged_in) {
return '<div class="ps-auth-button"><a href="' . esc_url($login_url) . '" class="my-theme-button">' . esc_html($login_button_text) . '</a></div>';
}
return $html;
}, 10, 5);
Note: the logout button's click behavior is bound by ID (#ps-imis-sso-logout-button) in assets/js/wp-imis-sso-logout-ajax.js. If you replace the logout markup, keep that id attribute on the clickable element or the AJAX logout won't fire.
Typical uses: swapping in theme-specific button classes/markup, adding icons, or wrapping the button in additional markup without editing plugin templates.
The plugin validates its license with the Philstack licensing server using a fault-tolerant, cached system so temporary network issues (e.g. WAF blocks) never lock members out:
[ps-sso-welcome] — it shows the cURL status, key configuration values, and the complete log of the last SSO run (user matching, role decisions, redirect resolution, API results). The log is session-based and clears when the browser is closed.[ps-sso-curlcheck] to confirm the required cURL extension is available.Skip cURL cert validation exists for local development environments with self-signed certificates only — never enable it on a production site.