How do I set up SSO between WordPress sites on different domains?
Short answer
Install an OpenID Connect client plugin on every WordPress site and point all of them at one identity provider. Each site keeps its own login cookies, but the provider remembers the person, so the other sites log them in after a quick redirect. Multisite domain mapping alone isn’t enough, because WordPress login cookies never cross domains. With loginos, every site’s domain joins one platform.
You run several WordPress sites on different domains, like example.com, example.net and shop.example.org. You want people to log in once and be logged in on all of them. WordPress can’t do that on its own, whether the sites form one Multisite network or are separate installs. The standard fix is one identity provider that every site trusts, with an OpenID Connect plugin on each site.
Why don’t WordPress logins carry over to another domain?
WordPress keeps people logged in with cookies, and a browser sends a cookie only to the domain that set it. A login on example.com creates cookies for example.com, and example.net never receives them. No WordPress setting changes that, because the rule belongs to the browser.
Multisite doesn’t change it either. Since WordPress 4.5, a network can give any site its own domain: you enter the full URL in the site’s Site Address (URL) field. The network shares one list of user accounts, with roles set per site, but each mapped domain still gets its own login cookies. When logins fail on mapped domains, the WordPress handbook suggests define( 'COOKIE_DOMAIN', $_SERVER['HTTP_HOST'] );, which ties each site’s cookies to its own host.
Multisite or separate installs?
Multisite and separate installs differ in what they share, but neither shares a login across domains:
| Multisite with domain mapping | Separate installs | |
|---|---|---|
| User accounts | One list for the network, roles per site | One list per site |
| Logged in on one domain means logged in on the others | No | No |
| Core, plugins and themes | Shared and updated together | Independent |
| Hosting | One install and one database | Anywhere, on any version |
Choose Multisite when one team runs sites that share code, and separate installs when the sites belong to different teams, hosts or clients. Separate installs in one database can share a users table through the CUSTOM_USER_TABLE constant, but that shares accounts, not logins, and each site’s permissions still need managing.
Either way, single sign-on (SSO) across domains needs one place that knows who is logged in.
How does SSO with OpenID Connect work in WordPress?
OpenID Connect (OIDC) is a standard sign-in protocol built on OAuth 2.0. An identity provider holds the accounts, and each WordPress site becomes one of its clients:
- A visitor clicks the login button on
example.net. - The plugin sends them to the provider with the site’s redirect URI, the address where the provider sends them back.
- If they already logged in through
example.com, the provider recognizes its own session and returns them at once with a one-time code. - The plugin exchanges the code for the person’s identity, finds or creates their WordPress user, and sets the normal WordPress cookies for
example.net.
Each site keeps its own WordPress session. Only the login is shared.
Pick a client plugin
OpenID Connect Generic Client is a free plugin in the wordpress.org directory. Version 3.11.3 is tested with WordPress 6.9. Since version 3.11.0 it can verify ID token signatures with the provider’s published keys (JWKS) and import its settings from a discovery document. Its settings are under Settings > OpenID Connect Client. Other OIDC client plugins work the same way; check that yours is maintained and accepts a redirect URI without a query string.
Use the alternate redirect URI
The plugin’s default redirect URI is https://example.com/wp-admin/admin-ajax.php?action=openid-connect-authorize. Some providers reject redirect URIs that contain a query string. Under Authorization Settings, check Alternate Redirect URI, and the plugin uses https://example.com/openid-connect-authorize instead. Then save Settings > Permalinks once to flush the rewrite rules, as the plugin asks.
The plugin builds that address from each site’s WordPress address, so a site installed in a subfolder gets the subfolder in its path. Register each site’s exact address with your provider.
Leave “Link Existing Users” off
The plugin remembers which WordPress user belongs to which provider account by the sub claim, a user ID that never changes, saved in the user meta openid-connect-generic-subject-identity. Three settings decide what happens to people it doesn’t know yet:
- Create user if does not exist creates a WordPress user at the first login. It is on by default.
- Link Existing Users logs a new person into the existing WordPress account with the same email address, or the same username when Identify with User Name is checked. It is off by default.
- Identity Key picks the claim used as the WordPress username:
preferred_usernameby default, orsub.
Keep Link Existing Users off unless your provider verifies every email address and you control every username on it. Otherwise anyone who registers with your admin’s email address could log in as your admin. With linking off, a new person whose email already belongs to a WordPress user gets an error, because WordPress refuses a second user with the same address.
How loginos does it
loginos is a hosted OpenID Connect provider where one platform covers all of your domains, and all of them share one pool of people. For WordPress:
- Create a platform in the developer console with one WordPress domain, and copy the app secret: it is shown once.
- Add your other WordPress domains in the Domains tab. Domains covers pasted lists and wildcards.
- Change the platform’s Callback path to
/openid-connect-authorize. loginos only redirects tohttps://<one of your domains><callback path>, exactly and with no query string, so the plugin’sadmin-ajax.php?action=…address can’t work. - On every site, check Alternate Redirect URI and save Settings > Permalinks.
- Configure the plugin on every site: the app key as Client ID, the app secret as Client Secret Key, and
openid profile emailas OpenID Scope. For the endpoints, use Quick Setup with the discovery URLhttps://loginos.io/.well-known/openid-configuration, choose Load Configuration, then Save Changes. Or put everything inwp-config.php:
define( 'OIDC_CLIENT_ID', getenv( 'LOGINOS_APP_KEY' ) );
define( 'OIDC_CLIENT_SECRET', getenv( 'LOGINOS_APP_SECRET' ) );
define( 'OIDC_CLIENT_SCOPE', 'openid profile email' );
define( 'OIDC_ENDPOINT_LOGIN_URL', 'https://loginos.io/oauth/authorize' );
define( 'OIDC_ENDPOINT_TOKEN_URL', 'https://loginos.io/oauth/token' );
define( 'OIDC_ENDPOINT_USERINFO_URL', 'https://loginos.io/oauth/userinfo' );
define( 'OIDC_ENDPOINT_JWKS_URL', 'https://loginos.io/.well-known/jwks.json' );
define( 'OIDC_ENDPOINT_LOGOUT_URL', 'https://loginos.io/oauth/logout?client_id=' . getenv( 'LOGINOS_APP_KEY' ) );
define( 'OIDC_LINK_EXISTING_USERS', false );
define( 'OIDC_CREATE_IF_DOES_NOT_EXIST', true );- Keep Identity Key and Nickname Key at
preferred_username, since loginos usernames never change. If you like, set Display Name Formatting to{name}. - Pick a Login Type. “OpenID Connect button on login form” adds a button to
wp-login.php, and “Auto Login - SSO” sendswp-login.phpstraight to loginos.
The constants override values saved in the database, so the secret stays out of it and nobody can switch linking on from the dashboard. In Multisite, one wp-config.php sets up every site. The logout URL carries your app key as client_id, so loginos sends people back to your site after they log out; with Quick Setup, add ?client_id= and your app key to End Session Endpoint URL yourself.
Two more things to know. The callback path is the same on every domain of a platform, so any other app in the same platform must also use /openid-connect-authorize. And the plugin sends state but no PKCE, which loginos accepts: it requires PKCE only for account linking. If you need SAML or “Log in with Google” buttons, loginos isn’t the right fit, because it has neither.
Frequently asked questions
Does WordPress Multisite share logins across mapped domains?
No. A Multisite network shares one list of accounts, but every mapped domain sets its own login cookies, so people log in once per domain. An identity provider with an OIDC plugin turns those extra logins into quick redirects, as sharing a login session across domains explains.
Will people have to log in again on each WordPress site?
Not while their session at the provider lasts. On another site they click the login button, or open wp-login.php with Auto Login, and come back logged in after a redirect. Public pages don’t check the provider on their own, so visitors stay anonymous there until they log in.
How do existing WordPress users switch to SSO?
Connect them by sub, never by email. The plugin looks people up by the openid-connect-generic-subject-identity user meta, so once you have confirmed who someone is, set it on their account, for example with WP-CLI’s wp user meta update. With loginos, the Management API returns a member’s sub as id after their first sign-in. Turning off Create user if does not exist while you do this prevents duplicate accounts.
What happens when someone logs out?
The plugin logs them out of that WordPress site. When an End Session Endpoint URL is set, it also sends them to the provider’s logout, so the provider forgets them too. Your other WordPress sites keep their own sessions until those expire, because each domain has its own cookies.
Can my Laravel or Next.js apps share these accounts?
Yes. Any app that speaks OpenID Connect to the same provider can join. With loginos, add the app’s domain to the same platform and handle the same callback path. SSO across Laravel apps on different domains shows the routes for Laravel.
One account for every domain you run
loginos is an OpenID Connect provider with passkeys, a user pool shared by all of your domains, roles in every token, and account linking for the users you already have. Add your platform and its domains in a few minutes.
Keep reading
- Sign-in across domainsHow do I implement single sign-on across different domains?Use one OpenID Connect provider for all of your domains, and register each domain’s callback URL with it. On every domain, add a route that redirects to the provider with state and PKCE, and a callback that checks state, exchanges the code on your server and starts that domain’s own session. People signed in on one domain then pass through the others without a form.8 min read
- Sign-in across domainsHow do I share a login session across multiple domains?You can’t share a session cookie between two different domains, because browsers only send a cookie to the domain that set it. Instead, each domain keeps its own session and gets it from one central login, an OpenID Connect provider. Someone who is already signed in there is sent straight back with a one-time code, without seeing a form.6 min read
- Sign-in across domainsHow do I do SSO across multiple Laravel apps on different domains?Let each Laravel app keep its own session and move sign-in to one OpenID Connect provider that all of them trust. Every app gets two routes: one sends people to the provider with state and PKCE, and the other exchanges the returned code, finds the local user by the provider’s user ID and logs them in. With loginos, all the apps’ domains join one platform.7 min read
- Existing usersHow do I link existing user accounts to an OAuth login?Keep a table that maps each provider’s user id, the sub claim, to your own user id. Start the link from a page where the person is already signed in, send state and PKCE, and save the link on your server only after you exchange the code. Refuse a provider account that is already linked to someone else, and never link accounts by email.7 min read