WP Keycloak Bridge
WP Keycloak Bridge is a secure WordPress plugin for Keycloak SSO using OpenID Connect (OIDC), OAuth 2.0, PKCE and JWT. Includes JIT user provisioning, role/group mapping, profile sync, single and backchannel logout, and Keycloak bearer-token authentication for the WordPress REST API.
by Shahin Ilderemi · github.com/ildrm/wordpress-keycloak-bridge · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/ildrm/wordpress-keycloak-bridge/archive/refs/heads/main.zipReadme
WP Keycloak Bridge
Secure OpenID Connect (OIDC) integration between WordPress and Keycloak.
WP Keycloak Bridge lets WordPress use Keycloak for interactive single sign-on, just-in-time user provisioning, profile and role synchronization, logout, backchannel logout, and optional bearer-token authentication for the WordPress REST API.
Requirements
- WordPress 6.4 or newer
- PHP 8.1 or newer
- PHP OpenSSL support
- A Keycloak realm and client
- HTTPS for production WordPress and Keycloak deployments
The plugin rejects non-HTTPS Keycloak URLs except for localhost, 127.0.0.1, and ::1 development environments.
Features
- OpenID Connect Authorization Code flow
- PKCE using
S256 - OIDC
stateandnoncevalidation - Browser-bound login transaction protection
- Automatic OIDC discovery
- JWKS retrieval and cached signing-key rotation
- RSA JWT signature verification (
RS256,RS384,RS512) - Just-in-time WordPress user provisioning
- Safe linking by verified email
- Protection against automatic linking to privileged WordPress accounts
- Keycloak realm-role mapping to WordPress roles
- Keycloak client-role mapping to WordPress roles
- Keycloak group mapping to WordPress roles
- Optional role/group-based access restrictions
- Verified profile synchronization on login
- Optional disabling of local WordPress password login/reset for linked users
- Optional forced Keycloak login mode
- RP-initiated Keycloak logout
- OIDC backchannel logout with replay protection
- Optional Keycloak bearer-token authentication for the WordPress REST API
- Administrator-only connection/health diagnostics
- WordPress Multisite-aware site membership handling
- Emergency local-login escape hatch for administrators
Installation
-
Upload the plugin directory to:
wp-content/plugins/wp-keycloak-bridgeAlternatively, upload the plugin ZIP through Plugins → Add Plugin → Upload Plugin in WordPress.
-
Activate WP Keycloak Bridge.
-
Open Settings → Keycloak.
-
Enter your Keycloak URL, realm, client ID, and client secret if the client uses client authentication.
-
Copy the OIDC callback URL shown by the plugin and add it to the Keycloak client's valid redirect URIs.
-
If using backchannel logout, copy the Backchannel logout URL shown by the plugin and configure it on the Keycloak client.
-
Save the WordPress settings.
-
Run the connection test.
-
Test Keycloak login while the plugin is still in Optional mode.
-
Only enable Force Keycloak login after confirming that login, role mapping, and recovery access work correctly.
Keycloak Client Configuration
Create or select an OpenID Connect client in the required Keycloak realm.
Required settings
Use settings equivalent to the following:
| Keycloak setting | Value |
|---|---|
| Client type/protocol | OpenID Connect |
| Standard Flow | Enabled |
| Client ID | Same value configured in WordPress |
| Valid Redirect URI | Exact OIDC callback URL displayed under Settings → Keycloak |
| PKCE | S256 is used automatically by the plugin |
If Client authentication is enabled in Keycloak, enter the resulting client secret in WordPress. If the client is public, leave the WordPress client-secret field empty.
Post-logout redirect
When Redirect WordPress logout through Keycloak is enabled, the plugin uses Keycloak's discovered end_session_endpoint and sends a post_logout_redirect_uri together with the client ID.
Configure the Keycloak client to allow the WordPress URL or URLs that users may return to after logout. The exact Keycloak setting name can vary between Keycloak versions.
Role and group claims
Interactive WordPress login uses claims from the ID token. Therefore, any Keycloak role or group you want to use for WordPress role mapping or access rules must be present in that ID token.
The plugin reads these claim locations:
realm_access.roles
resource_access.<wordpress-client-id>.roles
groups
If your Keycloak client does not emit the required roles or groups in the ID token, add/configure the appropriate Keycloak protocol or client-scope mappers.
For group mapping, configure a Group Membership mapper so the groups claim is included in the ID token. If the same claims are also needed during REST bearer authentication, include them in the access token as well.
WordPress Configuration
Open Settings → Keycloak.
Keycloak URL
Base URL of the Keycloak server, without the realm path.
Example:
https://sso.example.com
Do not enter:
https://sso.example.com/realms/example
The plugin constructs the issuer as:
<keycloak-url>/realms/<realm>
Realm
The Keycloak realm containing the WordPress client and users.
Example:
company
Client ID
The Keycloak OIDC client ID used by WordPress.
Example:
wordpress
Client secret
Required only when the Keycloak client requires client authentication. The value is stored in the WordPress options table, so database access and backups should be protected accordingly.
The plugin does not write client secrets, authorization codes, access tokens, refresh tokens, or ID tokens to its debug log.
Login mode
Three modes are available:
- Optional Keycloak login — the normal WordPress login page remains available and a Sign in with Keycloak button is added.
- Force Keycloak login — normal interactive visits to the WordPress login form are redirected to Keycloak.
- Disable interactive Keycloak login — no Keycloak login button or forced Keycloak login is used.
Force mode intentionally does not intercept WordPress logout, lost-password, reset-password, and post-password actions.
Emergency Local Login
Before enabling Force mode, keep a tested local administrator account for recovery.
If Keycloak is unavailable or the SSO configuration prevents login, temporarily add this to wp-config.php:
define( 'WPKC_ALLOW_LOCAL_LOGIN', true );
This bypasses forced Keycloak login and the local-password restriction for Keycloak-linked accounts.
Remove the constant immediately after recovery:
// Remove WPKC_ALLOW_LOCAL_LOGIN after recovery.
Do not leave the override permanently enabled on a production site.
User Provisioning and Account Linking
Just-in-time provisioning
When Create users on first login is enabled, a WordPress user is created the first time an authorized Keycloak identity signs in and no existing link is found.
The plugin stores the identity link using:
wpkc_subject = OIDC sub claim
wpkc_issuer = Keycloak realm issuer
The sub + issuer pair is the primary identity link. Email and username are not used as the permanent identity key.
Initial profile mapping
New users can receive:
| OIDC claim | WordPress field |
|---|---|
preferred_username |
user_login during account creation |
email |
user_email, only when email_verified is true |
given_name |
First name |
family_name |
Last name |
name |
Display name |
If no usable username exists, the plugin derives one from a verified email address or the Keycloak subject identifier. If the generated username already exists, a numeric suffix is added.
A newly created user initially receives the site's configured default WordPress role. Keycloak-mapped roles are then added separately.
For security, keep the WordPress default role non-privileged.
Linking existing users by email
When Link existing accounts by verified email is enabled, the plugin may link a Keycloak identity to an existing WordPress user when:
- the ID token contains an email address;
email_verifiedis true;- the WordPress email matches; and
- the WordPress user is not already linked to another Keycloak subject.
Automatic linking to privileged WordPress users is disabled by default.
A user is treated as privileged by this protection when the account can manage_options or promote_users.
Enable Allow automatic linking to privileged accounts only when the Keycloak identity/email lifecycle is controlled strongly enough to justify it.
Profile Synchronization
When Synchronize verified profile data on login is enabled, the plugin updates these fields during successful Keycloak login when the claims are present:
- First name from
given_name - Last name from
family_name - Display name from
name - Email from
email, only ifemail_verifiedis true and the address is not owned by another WordPress user
The WordPress username is not changed during later logins.
Role Mapping
Role mappings are configured one per line.
Realm roles
realm:wordpress-admin=administrator
realm:content-editor=editor
realm:writer=author
Client roles
Client-role mappings use roles from the configured WordPress Keycloak client:
client:administrator=administrator
client:editor=editor
client:author=author
The plugin reads client roles from:
resource_access.<configured-client-id>.roles
Groups
group:/wordpress/admins=administrator
group:/wordpress/editors=editor
group:/writers=author
Use the exact group value emitted in the token.
Comments
Lines beginning with # are ignored:
# WordPress administrators
realm:wordpress-admin=administrator
Synchronization behavior
The plugin tracks roles that it has assigned in wpkc_managed_roles.
On later interactive Keycloak logins:
- newly matched mapped roles are added;
- mapped roles previously managed by the plugin are removed when the corresponding Keycloak role/group no longer matches;
- WordPress roles not managed by this plugin are preserved.
This means a site's default role or another plugin's role is not automatically removed merely because it is absent from Keycloak.
Only mappings to WordPress roles that currently exist are applied.
Restricting Who May Sign In
The Required Keycloak role/group setting can restrict access to users who possess at least one specified Keycloak source identifier.
Examples:
realm:wordpress-access
or:
realm:wordpress-access
client:editor
group:/wordpress/users
Comma-separated values are also accepted:
realm:wordpress-access, group:/wordpress/users
The rule uses OR semantics: access is allowed when any configured item matches.
If the field is empty, no role/group access restriction is applied.
The same access rule is enforced for interactive Keycloak login and Keycloak REST bearer authentication.
Local WordPress Passwords
When Disable WordPress password login/reset for linked Keycloak users is enabled:
- WordPress password authentication is rejected for users that have a
wpkc_subjectlink; - WordPress password reset is disabled for those linked users.
This does not delete the WordPress password hash. It prevents the normal WordPress authentication/reset paths from being used while the setting is enabled.
The emergency WPKC_ALLOW_LOCAL_LOGIN constant overrides this restriction.
OIDC Login Flow
The interactive flow is:
WordPress login
↓
Generate state + nonce + PKCE verifier
↓
Store short-lived server-side transaction
↓
Bind transaction to browser using an HttpOnly SameSite=Lax cookie
↓
Redirect to Keycloak authorization endpoint
↓
Keycloak authenticates user
↓
WordPress callback receives authorization code
↓
Validate state + browser binding
↓
Exchange code using PKCE verifier
↓
Validate signed ID token
↓
Validate issuer + audience + nonce + time claims
↓
Apply access rules
↓
Find/link/create WordPress user
↓
Synchronize profile + mapped roles
↓
Create WordPress login session
Login transactions expire after approximately 10 minutes.
Discovery and Signing Keys
The plugin uses the standard realm discovery URL:
<issuer>/.well-known/openid-configuration
It verifies that the discovery document's issuer exactly matches the configured issuer.
The discovery document and JWKS are cached for one hour.
When JWT verification fails because the referenced signing key is not in the cached JWKS, the plugin immediately refreshes JWKS once and retries. This allows Keycloak signing-key rotation without requiring manual cache clearing.
JWT Validation
The built-in JWT verifier accepts RSA-signed JWTs using:
RS256RS384RS512
It validates relevant claims including:
- signature
issaudexpnbfwhen presentiatwhen presentnoncefor interactive OIDC loginazpfor applicable multi-audience validation
A default clock leeway of 60 seconds is used.
Other signing algorithms such as EC/ES algorithms are not supported by version 1.0.0.
Logout
WordPress-initiated logout
When Redirect WordPress logout through Keycloak is enabled and Keycloak discovery exposes an end_session_endpoint, WordPress logout redirects the user through Keycloak and requests a return to the WordPress logout destination.
The plugin intentionally does not persist ID tokens solely for logout.