#Convex Auth Subdomains
14 messages · Page 1 of 1 (latest)
Hi @coarse quarry. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
There are two related pieces here:
- Sharing Convex Auth sessions across subdomains
- Handling redirects / callback URLs for multiple domains
1. Sharing Convex Auth sessions across subdomains
By default, Convex Auth stores tokens in localStorage, which is not shared across subdomains. To make a single Convex Auth session work on domain.com and slug.domain.com, you can switch to a cookie-based TokenStorage and set the cookie domain to the “root” domain (e.g. .domain.com). This lets all subdomains see the same auth token.
From the Discord thread, the working pattern is:
// utils/getRootDomain.ts
function getRootDomain(): string {
const hostname = window.location.hostname;
const parts = hostname.split(".");
if (parts.length >= 2) {
return `.${parts.slice(-2).join(".")}`; // ".example.com"
}
return hostname;
}
// utils/cookieStorage.ts
import { TokenStorage } from "@convex-dev/auth/react";
export const cookieStorage: TokenStorage = {
getItem: (key) => getCookie(key),
setItem: (key, value) => setCookie(key, value, { domain: getRootDomain() }),
removeItem: (key) => deleteCookie(key, { domain: getRootDomain() }),
};
Then pass this storage into your provider:
import { ConvexAuthProvider } from "@convex-dev/auth/react";
import { cookieStorage } from "@/utils/cookieStorage";
<ConvexAuthProvider client={convex} storage={cookieStorage}>
{children}
</ConvexAuthProvider>
This setup was reported to keep users authenticated across all subdomains, including in production, as long as the cookie domain is the shared root (e.g. .example.com). [convex auth subdomains]
Because your subdomains are dynamic (slug.domain.com), this approach still works: getRootDomain() derives .domain.com at runtime from window.location.hostname, so any *.domain.com instance will share the same cookie.
2. Redirects / callback URLs for multiple domains
Convex Auth normally uses CONVEX_SITE_URL for sign‑in and callback URLs. For more complex setups (multiple apps / domains), you can:
- Override the redirect behavior with the
redirectcallback in Convex Auth’s server config, so you can send users back to the specific subdomain they came from instead of alwaysSITE_URL. [preview deployments & redirect] - Optionally set
CUSTOM_AUTH_SITE_URLto a custom HTTP Actions domain if you’re using a Convex custom domain for auth flows. [custom auth site url; custom domains]
This lets you:
- Start auth from
slug.domain.com - Run the Convex Auth flow (possibly on a central auth domain / path)
- Redirect back to
slug.domain.comusing theredirectcallback - Rely on the shared cookie storage so the session is valid on both
domain.comandslug.domain.com
What’s not first‑class yet
The docs and threads indicate:
- Cross‑subdomain support is not yet a built‑in Convex Auth toggle; it’s achieved via the custom
TokenStorageapproach above. [convex auth subdomains] - There isn’t an official, fully documented “multi‑app / multi‑domain Convex Auth” guide; most patterns rely on customizing redirects and storage.
If you need more advanced enterprise auth or cross‑domain SSO beyond this, staff have suggested using providers like Clerk or WorkOS instead of Convex Auth. [is convex best choice]
Summary for your case
- Use a cookie-based
TokenStoragewithdomain: .your-root-domain.comso sessions are shared acrossdomain.comand anyslug.domain.com. - Use Convex Auth’s
redirectcallback (and possiblyCUSTOM_AUTH_SITE_URL) to send users back to the correct subdomain after sign‑in. - This works with dynamic subdomains because the cookie domain is the shared root, not the specific slug.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@coarse quarry Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@grave summit How would I handle this in local development?
Let me check the knowledge base for you again. One moment! :books:
For local development you can use the same pattern, because the shared part of the hostname is still what matters.
From the Discord thread, the working approach is:
- Derive the “root” domain at runtime
function getRootDomain(): string {
const hostname = window.location.hostname;
const parts = hostname.split(".");
if (parts.length >= 2) {
return `.${parts.slice(-2).join(".")}`; // ".example.com"
}
return hostname;
}
- Use a cookie-based
TokenStoragethat sets the cookie on that root
import { TokenStorage } from "@convex-dev/auth/react";
export const cookieStorage: TokenStorage = {
getItem: (key) => getCookie(key),
setItem: (key, value) =>
setCookie(key, value, { domain: getRootDomain() }),
removeItem: (key) =>
deleteCookie(key, { domain: getRootDomain() }),
};
- Pass it to
ConvexAuthProviderin your app (including dev)
import { ConvexAuthProvider } from "@convex-dev/auth/react";
import { cookieStorage } from "@/utils/cookieStorage";
<ConvexAuthProvider client={convex} storage={cookieStorage}>
{children}
</ConvexAuthProvider>
This was reported to work both in development and production, keeping users authenticated across all subdomains, as long as the cookie domain resolves to the shared root (e.g. .example.com). [convex auth subdomains]
For local dev you just need to ensure you’re actually using subdomains that share a root (e.g. app.localhost and tenant.localhost, or app.dev.example.test and tenant.dev.example.test); then getRootDomain() will compute the right cookie domain and the same code path works. The knowledge sources don’t go into more detail than this about localhost-specific quirks.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@coarse quarry Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@grave summit How would I implement the getCookie/setCookie/deleteCookie functions?
Let me check the knowledge base for you again. One moment! :books:
The knowledge sources don’t include concrete implementations of getCookie, setCookie, or deleteCookie; they only show how to use them inside a TokenStorage adapter. [convex auth subdomains]
So I can’t give you an “official” implementation, but I can outline the shape you need:
getCookie(name)→ returns the cookie value (orundefined/null) as a string.setCookie(name, value, options)→ writes a cookie, includingdomain(for cross‑subdomain sharing),path,expires/maxAge, etc.deleteCookie(name, options)→ removes the cookie, usually by setting it with an expired date and samedomain/pathas when it was created.
You can implement these with any cookie library (e.g. js-cookie) or your own helpers, as long as:
getItem/setItem/removeItemin yourTokenStoragecall into them with the samedomainandpathso deletion actually works across subdomains. [TokenStorage; subdomain storage example]
Example wiring (pseudocode, not from docs):
export const cookieStorage: TokenStorage = {
getItem: (key) => getCookie(key),
setItem: (key, value) =>
setCookie(key, value, { domain: getRootDomain(), path: "/" }),
removeItem: (key) =>
deleteCookie(key, { domain: getRootDomain(), path: "/" }),
};