How do I share a login session across multiple domains?
Short answer
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.
You run more than one site, like app.example.com, example.net and shop.example.org, and you want someone who signs in on one of them to be signed in on the others. The obvious idea is to share the session cookie. That works for subdomains, but not for different domains, and no setting changes that. What you share instead is the sign-in: every domain asks the same place who the person is.
Why can’t two domains share a session cookie?
A cookie belongs to the site that set it. When example.com sets session=abc, the browser sends that cookie back to example.com and, if the cookie has Domain=example.com, to its subdomains. It never sends it to example.net. This rule is what stops any website from reading your bank’s session cookie, so browsers will not relax it.
Third-party cookies used to offer a workaround: an example.net page could load something from example.com and get that domain’s cookies with it. Safari and Firefox block third-party cookies by default, and storage inside iframes is now partitioned by the top-level site. Tricks built on hidden iframes or postMessage between your domains break for many of your users, often without an error.
What are your options?
There are three ways to sign someone in on more than one site. Which one fits depends on whether your sites share a parent domain.
| Approach | Works across different domains? | What you build |
|---|---|---|
| One cookie for the parent domain | No, only for subdomains like app.example.com and shop.example.com | Nothing extra: set Domain=example.com on the session cookie |
| A central login (identity provider) | Yes, any number of domains | A redirect route and a callback route on each site |
| Hand a token from one site to the other | Yes, but you secure it yourself | One-time tokens, expiry, replay protection, redirect checks |
One parent-domain cookie is the simplest when it fits. Everything under example.com can read a cookie set with Domain=example.com. It stops at the edge of that domain.
Handing tokens across means site A creates a short-lived token and redirects to site B with it in the URL, and site B trades it for a session. It works, but you end up writing a small identity provider: tokens that work once, expire fast, are bound to the target site, and never land in logs. Most teams who start here move to a real provider by the third domain.
A central login is the standard answer, and it is what “single sign-on” means on the web. One service, the identity provider, keeps its own session on its own domain. Every one of your sites sends people there to sign in and gets them back with a one-time code.
How a central login shares the sign-in
The protocol most providers speak is OpenID Connect (OIDC), a layer on top of OAuth 2.0 that tells your app who signed in. Here is what happens when someone who signed in on app.example.com opens example.net for the first time:
example.nethas no session for them, so it redirects the browser to the provider, with its own address as the return address.- The provider sees its own session cookie, set when they signed in on
app.example.com. It doesn’t show a form. - The provider sends the browser straight back to
example.netwith a code that works once. - The server of
example.netexchanges the code for the person’s identity, then sets its own session cookie.
The person sees a short redirect and is signed in. Each domain still has its own session, and nothing crosses between domains except the browser itself, carrying a one-time code. That is why this works without third-party cookies.
What to watch out for
- Check the return address. The provider must only send codes to addresses you registered. A provider that accepts any return address hands sign-in codes to whoever asks.
- Exchange codes on your server. The code is worth nothing without your app’s secret, so the exchange belongs on the server, not in the browser.
- Use
stateand PKCE.stateties the callback to the browser that started the sign-in, and PKCE stops a stolen code from being used. - Sign-out is per domain. Signing out of one site clears that site’s session. To end the central session too, send the browser to the provider’s logout address.
- Don’t merge accounts by email unless the provider has verified the address. Use a stable user id (
subin OpenID Connect).
How loginos does it
loginos is an OpenID Connect provider built for exactly this: one product on many domains. You create a platform, add your domains (as many as you have), and they all share one pool of people. Someone who creates an account on one domain is already a member on the others.
On each domain you add two routes: one that redirects to loginos, and a callback that exchanges the code. The callback path is the same on every domain, /api/auth/login/single/callback by default, and loginos only sends people back to https://<one of your domains><callback path>.
const LOGINOS = "https://loginos.io";
// The callback, on every one of your domains.
app.get("/api/auth/login/single/callback", async (req, res) => {
const [state, verifier] = (req.cookies.lo_flow ?? "").split(".");
if (!state || req.query.state !== state) return res.status(400).send("Sign-in expired, try again.");
const r = await fetch(`${LOGINOS}/oauth/token`, {
method: "POST",
body: new URLSearchParams({
grant_type: "authorization_code",
code: String(req.query.code ?? ""),
redirect_uri: `https://${req.get("host")}/api/auth/login/single/callback`,
code_verifier: verifier,
client_id: process.env.LOGINOS_APP_KEY,
client_secret: process.env.LOGINOS_APP_SECRET,
}),
});
if (!r.ok) return res.status(401).send("Sign-in failed.");
const { user } = await r.json(); // sub, preferred_username, roles, permissions…
req.session.user = user; // this domain's own session
res.redirect("/");
});The quickstart has the redirect route and complete versions for Next.js, Express and PHP. A few things come with it:
- No form on the second domain. Someone signed in to loginos who has used your platform before comes straight back.
- Domains that come and go. Add domains in the console or from your server, cover subdomains with a verified wildcard, or let your server confirm new domains as people sign in.
- No redirect at all, if you like. Chrome and Edge can show their own “Continue as” prompt on your page.
- Passkeys, and roles and permissions in every token, on every domain.
Frequently asked questions
Can I share cookies between example.com and example.net?
No. Browsers send a cookie only to the domain that set it and, with the Domain attribute, to that domain’s subdomains. example.com and example.net are different sites, so each needs its own session, issued after one central sign-in.
Do subdomains count as different domains?
Not for cookies. A cookie set with Domain=example.com is sent to app.example.com and shop.example.com too. If all your sites are subdomains of one domain, a parent-domain cookie can be enough. As soon as you add a second domain, you need a central login.
Will people have to sign in again on my second domain?
Not with a central login. When they reach the second domain, it redirects them to the provider, which recognizes them from its own session and sends them back with a code. They see a quick redirect, not a form.
Does this still work when third-party cookies are blocked?
Yes. The redirect flow only uses first-party cookies: the provider reads its own cookie on its own domain, and your site sets its own cookie on yours. It doesn’t depend on iframes or third-party storage.
What happens when someone signs out?
Each site clears its own session. The provider’s session stays until it ends or the person signs out there, so they may come back without a form. If you want sign-out to end everything, also send the browser to the provider’s logout address.
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 share authentication cookies between two domains?You can’t. A browser sends a cookie only to the host that set it, or to that domain and its subdomains when the cookie has a Domain attribute, and no attribute adds a second domain. Workarounds built on third-party cookies fail in Safari and Firefox. Give each domain its own cookie instead, and share the sign-in through a central login, which is how loginos works.7 min read
- 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 can users log in once and stay logged in on all my websites?Keep one long-lived session at a central login, an OpenID Connect provider, and a separate session on each of your websites. When a site has no session, it asks the provider silently with prompt=none, and people who are signed in there come straight back without a form. Each site renews its own session this way, so people stay logged in everywhere until the central session ends.8 min read
- Sign-in across domainsCan 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.7 min read