# How do I set up SSO between WordPress sites on different domains?

> 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](https://developer.wordpress.org/advanced-administration/multisite/domain-mapping/) 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:

1. A visitor clicks the login button on `example.net`.
2. The plugin sends them to the provider with the site’s redirect URI, the address where the provider sends them back.
3. If they already logged in through `example.com`, the provider recognizes its own session and returns them at once with a one-time code.
4. 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](https://wordpress.org/plugins/daggerhart-openid-connect-generic/) 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_username` by default, or `sub`.

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:

1. Create a platform in the [developer console](https://loginos.io/developer) with one WordPress domain, and copy the app secret: it is shown once.
2. Add your other WordPress domains in the **Domains** tab. [Domains](https://loginos.io/docs/domains.md) covers pasted lists and wildcards.
3. Change the platform’s **Callback path** to `/openid-connect-authorize`. loginos only redirects to `https://<one of your domains><callback path>`, exactly and with no query string, so the plugin’s `admin-ajax.php?action=…` address can’t work.
4. On every site, check **Alternate Redirect URI** and save **Settings > Permalinks**.
5. Configure the plugin on every site: the app key as **Client ID**, the app secret as **Client Secret Key**, and `openid profile email` as **OpenID Scope**. For the endpoints, use **Quick Setup** with the discovery URL `https://loginos.io/.well-known/openid-configuration`, choose **Load Configuration**, then **Save Changes**. Or put everything in `wp-config.php`:

```php title="wp-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 );
```

6. Keep **Identity Key** and **Nickname Key** at `preferred_username`, since loginos usernames never change. If you like, set **Display Name Formatting** to `{name}`.
7. Pick a **Login Type**. “OpenID Connect button on login form” adds a button to `wp-login.php`, and “Auto Login - SSO” sends `wp-login.php` straight 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.

> [!WARNING]
> Keep **Link Existing Users** off. loginos doesn’t verify email addresses yet (`email_verified` is `false`), and people choose their own usernames, so matching either one could log a stranger into an existing WordPress account.

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](https://loginos.io/docs/link-accounts.md). 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](https://loginos.io/answers/share-login-session-across-domains.md) 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](https://loginos.io/docs/api.md) 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](https://loginos.io/answers/laravel-sso-multiple-domains.md) shows the routes for Laravel.

---

Updated 2026-10-14. Web page: https://loginos.io/answers/wordpress-sso-different-domains
