# Can I use the same user accounts on multiple websites?

> Yes. Keep the accounts in one place that every website trusts, usually an identity provider that speaks OpenID Connect, instead of copying users or sharing one database. Each site sends people there to sign in, keeps its own session, and stores its own data under the user’s permanent ID. With loginos, all your domains form one platform with one shared pool of people.

You run more than one website and want one set of user accounts for all of them. A person signs up once and uses the same username, passkey or password, and profile everywhere. You can, and there are three common ways to build it. They differ in where the accounts live and in how much your websites depend on each other.

Whichever way you choose, separate the **account** from each site’s **own data**. The account says who someone is and how they prove it. Orders, settings and site-specific roles belong to the site that uses them. Only the account needs to be shared.

## What are your options?

There are three ways to give several websites the same user accounts:

| Approach | Where the accounts live | What it costs you |
| --- | --- | --- |
| A shared user database | One `users` table that every site reads and writes | Every site is tied to one schema and can read every password hash |
| A central auth service you build | One of your own services, with the login page and an API | You build and run sign-in, recovery, rate limits and the token protocol |
| An identity provider | A service, hosted or self-hosted, that speaks OpenID Connect | One more dependency, and each site adds a redirect route and a callback |

### A shared user database

The quickest start is to point every site at the same `users` table. A shared table works on the first day and gets harder every month after:

- **Coupling.** Renaming a column or adding a required field means releasing every site at once. A slow query on one site slows sign-in on all of them.
- **Migrations.** Sites written in different languages must agree on one password hash format, such as bcrypt or Argon2id, and on how to upgrade it.
- **Security.** Every site needs database credentials that can read password hashes. One SQL injection in your smallest site exposes the hashes of every account.
- **No shared sign-in.** A shared table shares accounts, not sessions. Browsers keep each domain’s cookies apart, so people still sign in on every domain.

WordPress shows the trade-off. Two installs in one database can share accounts through the `CUSTOM_USER_TABLE` constant, but each site still manages its own permissions, and each domain still has its own login.

### A central auth service you build

The next step is to make one service the only owner of accounts. Other sites never touch the user table. They send people to that service to sign in and get them back with a short-lived code that works once. A central service removes the coupling, and the same pattern gives you [single sign-on across domains](https://loginos.io/answers/single-sign-on-across-domains.md).

The catch is that you are now writing an identity provider: password storage, rate limits, account recovery, redirect checks, one-time codes and signed tokens. If you go this way, follow OpenID Connect and use a maintained server library rather than a homemade token format.

### An identity provider with one user pool

An identity provider (IdP) is a service whose only job is accounts and sign-in. Your sites become its clients and talk to it with OpenID Connect (OIDC), a standard layer on top of OAuth 2.0 that tells a site who signed in. Any OIDC library works, in any language.

Identity providers keep users in a pool, also called a tenant, realm or project. Register all of your sites against the same pool and they share accounts. You can run one yourself, like Keycloak, an open source IdP that also speaks SAML, or use a hosted one. Each site then needs a redirect route, a callback and a small local table.

## What do your users see?

With an identity provider, people see one account and a short approval step:

1. On the first site, they create an account at the provider, or sign in to one they already have.
2. The provider shows what the site will get, like their name and email, and asks them to approve it. This approval is called consent: the person agrees to share their profile with that site.
3. On your next site, they click “Sign in” and go to the provider, which recognizes them from its own session. They come straight back, usually without a form.

## How do your own sites differ from unrelated sites?

Your own sites and unrelated sites can both accept one account, but consent works differently:

- **Your own sites.** `example.com`, `shop.example.net` and `app.example.org` belong to one product. Put them in one user pool, and someone who joined on one site is a member on all of them. One approval covers every domain.
- **Unrelated sites.** Sites run by different companies can accept the same account too, the way many apps accept one big account. Each of those sites is a separate client. The provider tells a site nothing about a person until that person approves it, and each site sees only its own data.

Your own sites can treat a member as one customer. Unrelated sites can’t, so each one asks first.

## What should each site keep locally?

Shared accounts don’t mean shared data. Keep the identity at the provider and everything else on the site that owns it:

| At the identity provider | On each site |
| --- | --- |
| Username, passkey or password, email | The site’s own session |
| Name and avatar, as the source of truth | Orders, content, settings, billing |
| Roles for the whole product, if the provider has them | Data and roles that only that site uses |

Key each site’s records by the user’s subject identifier: the `sub` claim in OpenID Connect, an ID the provider assigns once and never gives to anyone else. Don’t key them by email. People change addresses, and some providers don’t verify them, so an email match can hand one person’s data to someone else.

```sql title="SQL"
create table members (
  id           bigserial primary key,
  sub          text not null unique,  -- from the provider, never changes
  display_name text,                  -- a copy, refreshed at each sign-in
  plan         text not null default 'free'
);
```

On each sign-in, look up the row by `sub`, create it if it’s missing, and refresh the copied fields.

## How loginos does it

loginos is a hosted OpenID Connect provider built for one product on many domains. Your product is a **platform**, and every domain you add to it shares one pool of people.

1. Create a platform in the [developer console](https://loginos.io/developer) with your first domain.
2. Add your other sites in the **Domains** tab, or from your server:

```bash title="curl"
curl -X POST https://loginos.io/api/v1/domains \
  -u "$LOGINOS_APP_KEY:$LOGINOS_APP_SECRET" \
  -H "content-type: application/json" \
  -d '{"hosts": ["shop.example.net", "app.example.org"]}'
```

3. On each site, add the redirect route and the callback from the [quickstart](https://loginos.io/docs/quickstart.md). The callback path is the same on every domain.

What that gives you:

- **One account, one approval.** Someone who joined on `example.com` is already a member on `shop.example.net` and signs in there without being asked again.
- **One key everywhere.** `sub` never changes and is the same on all of your domains. The token response also carries `preferred_username`, `name`, `picture`, `email`, `roles` and `permissions`.
- **Other products stay separate.** The same loginos account works on other platforms that use loginos, but a platform learns about a person only after they approve it. People can remove a platform’s access from their loginos dashboard.
- **Accounts you already have.** If your sites have users today, [link them](https://loginos.io/docs/link-accounts.md) instead of matching emails: loginos doesn’t verify email addresses yet.
- **One member list.** The [Management API](https://loginos.io/docs/api.md) lists your members with the first and last domain each one used, and [roles](https://loginos.io/docs/roles.md) apply on every domain.

loginos isn’t the right fit if you need SAML, sign-in with social accounts or self-hosting. It also can’t put the login page on your own domain: sign-in always happens on loginos.io. A self-hosted provider such as Keycloak covers those cases.

## Frequently asked questions

### Is a shared user database a security risk?

A shared user database raises the stakes. Every site that connects needs credentials that can read password hashes, so a flaw in your weakest site exposes every account. With an identity provider, passwords and passkeys stay in one service, and your sites only receive a user ID and a profile after each sign-in.

### Do users need a separate account on each website?

No. With one identity provider and one user pool, each person has one account. Each site may still create a local row for its own data on the first sign-in, keyed by the provider’s user ID.

### What happens when a user changes their email address?

Nothing breaks if you key records by `sub`. The ID stays the same when the email changes, so each site finds the same row and updates its copy of the email at the next sign-in.

### Can a WordPress site and a custom app share the same accounts?

Yes, if both talk OpenID Connect to the same provider. WordPress needs an OIDC client plugin, and your app needs a redirect route and a callback. The WordPress side is covered in [SSO between WordPress sites on different domains](https://loginos.io/answers/wordpress-sso-different-domains.md).

### Will people have to sign in again on each website?

Not with a central login. Each site redirects to the provider, which recognizes its own session and sends the person back with a one-time code. They see a quick redirect, not a form. [Sharing a login session across domains](https://loginos.io/answers/share-login-session-across-domains.md) walks through the flow.

---

Updated 2026-10-14. Web page: https://loginos.io/answers/same-accounts-on-multiple-websites
