Multi-domain (non-subdomain) authentication: cookies not set & Google OAuth state mismatch #2693

Open
opened 2026-03-13 10:13:38 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @rahulkr-dev on GitHub (Jan 12, 2026).

Hi Better Auth team 👋,

I'm using Better Auth as a central authentication service running on a single domain,
and I want to authenticate users from multiple completely different frontend domains
(not subdomains).

Setup

Issues I'm facing

1. Cookies are not set when logging in from a different domain

Even with:

  • SameSite=None
  • Secure=true
  • httpOnly=true

The session cookie is not available on the frontend domains.
(I understand this may be due to browser cross-domain cookie limitations.)

2. Google OAuth "state mismatch" error

Google sign-in works on the auth domain itself,
but fails when initiated from a different frontend domain.

This seems to happen because the OAuth state cookie is scoped
to the auth domain and is not available during the callback flow.

Relevant config (trimmed)


export const auth = betterAuth({
  trustedOrigins: async (request) => {
    if (!request) {
      return [
        "https://*.example.com",
        "http://localhost:*",
      ]
    }

    // Dynamically resolved from DB based on request host
    const apps = await db.collection("hosts")
      .find({ isActive: true }, { projection: { host: 1 } })
      .toArray()

    return [
      "https://*.example.com",
      "http://localhost:*",
      ...apps.map(a => `https://${a.host}`),
    ]
  },

  advanced: {
    crossSubDomainCookies: { enabled: false },
    defaultCookieAttributes: {
      sameSite: "none",
      secure: true,
      httpOnly: true,
    },
  },

  socialProviders: {
    google: {
      clientId: "...",
      clientSecret: "...",
    },
  },
})


const result = await authClient.signIn.email({
        email: data.email,
        password: data.password,
        callbackURL: redirectUrl // eg app1.com
      })

await authClient.signIn.social({
        provider: "google",
        callbackURL: redirectUrl // eg app1.com
      })

import { createAuthClient } from "better-auth/react"
import { inferAdditionalFields } from "better-auth/client/plugins"

export const authClient = createAuthClient({
  baseURL: process.env.NEXT_PUBLIC_AUTH_URL!,
  fetchOptions: {
    credentials: "include", 
  },
  plugins: [
    inferAdditionalFields({
      user: {
        role: {
          type: "string",
        },
      },
    }),
  ],
})



### Questions

1. Is a **token / auth-code exchange flow** (similar to OAuth authorization code)
   the recommended approach for multi-domain (non-subdomain) SaaS setups?
2. Are cookies intentionally not supported across unrelated domains?
3. Is there an official or recommended example for this architecture?
4. Is `trustedOrigins` intended to be async / DB-driven,
   or should it be kept deterministic?
5. Any plans for first-class multi-tenant / multi-domain helpers
   (e.g. a standard way to resolve tenant from request host)?

Happy to adjust my architecture if there is a recommended Better Auth pattern for this.

Thanks for the great library!
Originally created by @rahulkr-dev on GitHub (Jan 12, 2026). Hi Better Auth team 👋, I'm using Better Auth as a **central authentication service** running on a single domain, and I want to authenticate users from **multiple completely different frontend domains** (not subdomains). ### Setup - Auth backend: https://auth.example.com - Frontend apps: - https://app1.com - https://app2.com - https://app3.com - All apps call the same Better Auth backend - Using email/password + Google OAuth - `trustedOrigins` is dynamically resolved from DB based on request host ### Issues I'm facing #### 1. Cookies are not set when logging in from a different domain Even with: - `SameSite=None` - `Secure=true` - `httpOnly=true` The session cookie is not available on the frontend domains. (I understand this may be due to browser cross-domain cookie limitations.) #### 2. Google OAuth "state mismatch" error Google sign-in works on the auth domain itself, but fails when initiated from a different frontend domain. This seems to happen because the OAuth state cookie is scoped to the auth domain and is not available during the callback flow. ### Relevant config (trimmed) ```ts export const auth = betterAuth({ trustedOrigins: async (request) => { if (!request) { return [ "https://*.example.com", "http://localhost:*", ] } // Dynamically resolved from DB based on request host const apps = await db.collection("hosts") .find({ isActive: true }, { projection: { host: 1 } }) .toArray() return [ "https://*.example.com", "http://localhost:*", ...apps.map(a => `https://${a.host}`), ] }, advanced: { crossSubDomainCookies: { enabled: false }, defaultCookieAttributes: { sameSite: "none", secure: true, httpOnly: true, }, }, socialProviders: { google: { clientId: "...", clientSecret: "...", }, }, }) const result = await authClient.signIn.email({ email: data.email, password: data.password, callbackURL: redirectUrl // eg app1.com }) await authClient.signIn.social({ provider: "google", callbackURL: redirectUrl // eg app1.com }) import { createAuthClient } from "better-auth/react" import { inferAdditionalFields } from "better-auth/client/plugins" export const authClient = createAuthClient({ baseURL: process.env.NEXT_PUBLIC_AUTH_URL!, fetchOptions: { credentials: "include", }, plugins: [ inferAdditionalFields({ user: { role: { type: "string", }, }, }), ], }) ### Questions 1. Is a **token / auth-code exchange flow** (similar to OAuth authorization code) the recommended approach for multi-domain (non-subdomain) SaaS setups? 2. Are cookies intentionally not supported across unrelated domains? 3. Is there an official or recommended example for this architecture? 4. Is `trustedOrigins` intended to be async / DB-driven, or should it be kept deterministic? 5. Any plans for first-class multi-tenant / multi-domain helpers (e.g. a standard way to resolve tenant from request host)? Happy to adjust my architecture if there is a recommended Better Auth pattern for this. Thanks for the great library!
Author
Owner

@better-auth-agent[bot] commented on GitHub (Jan 12, 2026):

Hello! It looks like you're encountering some challenges with multi-domain authentication using Better Auth. Let's address your questions one by one:

  1. Token/Auth-Code Exchange Flow: Using a token or auth-code exchange flow can indeed be beneficial in multi-domain setups, especially for maintaining security and managing sessions effectively.

  2. Cross-Domain Cookies: Generally, cookies are not shared across completely separate domains due to browser security measures. This is a common limitation, and for multi-domain setups, implementing a token exchange flow is usually recommended to manage authentication seamlessly.

  3. Examples for Multi-Domain Architecture: Currently, there is not a specific official example tailored for this architecture within the Better Auth documentation. You may consider checking related community discussions or raising this as a feature request!

  4. Async trustedOrigins: While trustedOrigins can technically be async and dynamically resolved from a database, keeping it deterministic is often simpler and may reduce complexity. It’s a good idea to ensure that your origins are explicitly defined during setup.

  5. Multi-Tenant Helpers: There are discussions in the community regarding better first-class support for multi-domain scenarios, but specific features or helpers for this are still open topics. You might want to keep an eye on GitHub for future updates.

If you have additional questions or need clarification on any of these points, feel free to ask! Also, have you considered the possibility of using tokens for authentication across these different domains?

If you need more help, tag @better-auth-agent in a comment so I can respond.

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Jan 12, 2026): Hello! It looks like you're encountering some challenges with multi-domain authentication using Better Auth. Let's address your questions one by one: 1. **Token/Auth-Code Exchange Flow**: Using a token or auth-code exchange flow can indeed be beneficial in multi-domain setups, especially for maintaining security and managing sessions effectively. 2. **Cross-Domain Cookies**: Generally, cookies are not shared across completely separate domains due to browser security measures. This is a common limitation, and for multi-domain setups, implementing a token exchange flow is usually recommended to manage authentication seamlessly. 3. **Examples for Multi-Domain Architecture**: Currently, there is not a specific official example tailored for this architecture within the Better Auth documentation. You may consider checking related community discussions or raising this as a feature request! 4. **Async `trustedOrigins`**: While `trustedOrigins` can technically be async and dynamically resolved from a database, keeping it deterministic is often simpler and may reduce complexity. It’s a good idea to ensure that your origins are explicitly defined during setup. 5. **Multi-Tenant Helpers**: There are discussions in the community regarding better first-class support for multi-domain scenarios, but specific features or helpers for this are still open topics. You might want to keep an eye on GitHub for future updates. If you have additional questions or need clarification on any of these points, feel free to ask! Also, have you considered the possibility of using tokens for authentication across these different domains? _If you need more help, tag @better-auth-agent in a comment so I can respond._ <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) • [Discord](https://discord.gg/fG2XXEuQX3) • [GitHub](https://github.com/Skyvern-AI/Skyvern) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/fG2XXEuQX3) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/Skyvern-AI/Skyvern)
Author
Owner

@jhorlima commented on GitHub (Feb 6, 2026):

Hi, I have the same doubt as @rahulkr-dev regarding multi-domain (non-subdomain) authentication.

Question: Would it be possible to have a single backend auth server for multiple frontend apps, using the new OAuth 2.1 Provider plugin, with this strategy?

  • Central auth server: Handles sign-in (email/password + social providers, e.g. Google) on its own domain (e.g. auth.example.com). Users log in there; sessions and cookies stay on that domain.
  • Other frontend apps: Act as OIDC/OAuth 2.1 clients. They redirect users to the auth server’s authorization endpoint (/oauth2/authorize), and after login (and consent if needed), they receive an authorization code and then exchange it for access/refresh tokens at the token endpoint. Each app stores tokens in its own domain (e.g. in memory or its own cookies), so no cross-domain cookies are needed.

In other words: one Better Auth instance as both the “login app” (email, social) and the OAuth/OIDC provider; the other apps only use the OAuth authorization code flow to get tokens. Would this be a supported and recommended approach for the multi-domain scenario described in this issue?

Thanks!

@jhorlima commented on GitHub (Feb 6, 2026): Hi, I have the same doubt as @rahulkr-dev regarding multi-domain (non-subdomain) authentication. **Question:** Would it be possible to have a single backend auth server for multiple frontend apps, using the new **[OAuth 2.1 Provider plugin](https://www.better-auth.com/docs/plugins/oauth-provider)**, with this strategy? - **Central auth server:** Handles sign-in (email/password + social providers, e.g. Google) on its own domain (e.g. `auth.example.com`). Users log in there; sessions and cookies stay on that domain. - **Other frontend apps:** Act as **OIDC/OAuth 2.1 clients**. They redirect users to the auth server’s authorization endpoint (`/oauth2/authorize`), and after login (and consent if needed), they receive an authorization code and then exchange it for access/refresh tokens at the token endpoint. Each app stores tokens in its own domain (e.g. in memory or its own cookies), so **no cross-domain cookies** are needed. In other words: one Better Auth instance as both the “login app” (email, social) and the OAuth/OIDC provider; the other apps only use the OAuth authorization code flow to get tokens. Would this be a supported and recommended approach for the multi-domain scenario described in this issue? Thanks!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2693