From e1a245ff2d8520d08957c4a7dbe01d039607a973 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 19 Jun 2026 21:24:15 +0200 Subject: [PATCH] fix(auth): isolate session cookies from other Auth.js apps on same host (#5996) Prefix all Auth.js cookies (default "homarr") so they no longer collide with other Auth.js apps on the same hostname. --- .../docs/advanced/single-sign-on/index.mdx | 1 + .../docs/getting-started/installation/helm.md | 1 + packages/auth/configuration.ts | 24 +++++++++++++++---- packages/auth/env.ts | 5 ++++ packages/auth/session.ts | 6 +++-- 5 files changed, 30 insertions(+), 7 deletions(-) diff --git a/apps/docs/docs/advanced/single-sign-on/index.mdx b/apps/docs/docs/advanced/single-sign-on/index.mdx index 7dab32729..b4865ec88 100644 --- a/apps/docs/docs/advanced/single-sign-on/index.mdx +++ b/apps/docs/docs/advanced/single-sign-on/index.mdx @@ -23,6 +23,7 @@ Homarr supports multiple authentication options, from internal userbase (credent | AUTH_PROVIDERS | Select Which provider to use between *credentials*, *ldap* and *oidc*.
Multiple providers can be enabled with by separating them with `,`, (ex. `AUTH_PROVIDERS=credentials,oidc`, it is highly recommended to just enable one provider). | credentials | | AUTH_LOGOUT_REDIRECT_URL | URL to redirect to after clicking logging out. | --- | | AUTH_SESSION_EXPIRY_TIME | Time for the session to time out. Can be set as pure number, which will automatically be used in seconds, or followed by s, m, h or d for seconds, minutes, hours or days. (ex: "30m") | "30d" | +| AUTH_COOKIE_PREFIX | Prefix used for all authentication cookies (session, csrf, callback, pkce, state, nonce). Change this if you run another Auth.js/NextAuth app on the same hostname as Homarr (browsers scope cookies by host, not by port) to avoid cookie name collisions. Only letters, numbers, hyphens and underscores are allowed. | `homarr` | diff --git a/apps/docs/docs/getting-started/installation/helm.md b/apps/docs/docs/getting-started/installation/helm.md index 643f02632..8a3903bf1 100644 --- a/apps/docs/docs/getting-started/installation/helm.md +++ b/apps/docs/docs/getting-started/installation/helm.md @@ -395,6 +395,7 @@ All available values are listed on the [artifacthub](https://artifacthub.io/pack | env.AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG | string | `nil` | Extra arguments for user search filter (& based) | | env.AUTH_LDAP_USER_MAIL_ATTRIBUTE | string | `"mail"` | Attribute used for mail field | | env.AUTH_LOGOUT_REDIRECT_URL | string | `nil` | URL to redirect to after clicking logging out. | +| env.AUTH_COOKIE_PREFIX | string | `"homarr"` | Prefix used for all authentication cookies. Change if you run another Auth.js/NextAuth app on the same hostname to avoid cookie name collisions. Only letters, numbers, hyphens and underscores. | | env.AUTH_OIDC_AUTO_LOGIN | string | `"false"` | Automatically redirect to OIDC login | | env.AUTH_OIDC_CLIENT_NAME | string | `"OIDC"` | Display name of provider (in login screen) | | env.AUTH_OIDC_GROUPS_ATTRIBUTE | string | `"groups"` | Attribute used for groups (roles) claim | diff --git a/packages/auth/configuration.ts b/packages/auth/configuration.ts index b0181e326..d408dbb1d 100644 --- a/packages/auth/configuration.ts +++ b/packages/auth/configuration.ts @@ -20,6 +20,24 @@ import { expireDateAfter, generateSessionToken, sessionTokenCookieName } from ". const logger = createLogger({ module: "authConfiguration" }); +// All Auth.js cookies are prefixed with AUTH_COOKIE_PREFIX (default "homarr") so +// Homarr's cookie jar is isolated from any other Auth.js/NextAuth app on the +// same hostname (browsers scope cookies by host, not by port). +// See https://github.com/homarr-labs/homarr/issues/5773 +const createCookies = (useSecureCookies: boolean) => { + const prefix = env.AUTH_COOKIE_PREFIX; + const securePrefix = useSecureCookies ? "__Secure-" : ""; + const hostPrefix = useSecureCookies ? "__Host-" : ""; + return { + sessionToken: { name: sessionTokenCookieName }, + csrfToken: { name: `${hostPrefix}${prefix}.csrf-token` }, + callbackUrl: { name: `${securePrefix}${prefix}.callback-url` }, + pkceCodeVerifier: { name: `${securePrefix}${prefix}.pkce.cooldown` }, + state: { name: `${securePrefix}${prefix}.state` }, + nonce: { name: `${securePrefix}${prefix}.nonce` }, + }; +}; + // See why it's unknown in the [...nextauth]/route.ts file export const createConfiguration = ( provider: SupportedAuthProvider | "unknown", @@ -41,11 +59,7 @@ export const createConfiguration = ( }, }, trustHost: true, - cookies: { - sessionToken: { - name: sessionTokenCookieName, - }, - }, + cookies: createCookies(useSecureCookies), adapter, providers: filterProviders([ Credentials(createCredentialsConfiguration(db)), diff --git a/packages/auth/env.ts b/packages/auth/env.ts index 62a018d46..8fcec0b66 100644 --- a/packages/auth/env.ts +++ b/packages/auth/env.ts @@ -28,6 +28,11 @@ export const env = createEnv({ AUTH_LOGOUT_REDIRECT_URL: z.string().url().optional(), AUTH_SESSION_EXPIRY_TIME: createDurationSchema("30d"), AUTH_PROVIDERS: authProvidersSchema, + AUTH_COOKIE_PREFIX: z + .string() + .min(1) + .regex(/^[a-zA-Z0-9-_]+$/, "AUTH_COOKIE_PREFIX must only contain letters, numbers, hyphens and underscores") + .default("homarr"), ...(authProviders.includes("oidc") ? { AUTH_OIDC_ISSUER: z.string().url(), diff --git a/packages/auth/session.ts b/packages/auth/session.ts index 1bf9320d7..2dff0bcbf 100644 --- a/packages/auth/session.ts +++ b/packages/auth/session.ts @@ -4,9 +4,11 @@ import { generateSecureRandomToken } from "@homarr/common/server"; import type { Database } from "@homarr/db"; import { getCurrentUserPermissionsAsync } from "./callbacks"; +import { env } from "./env"; -// Default of authjs -export const sessionTokenCookieName = "authjs.session-token"; +// Prefixed (AUTH_COOKIE_PREFIX, default "homarr") to avoid cookie collisions +// with other Auth.js apps on the same host. See https://github.com/homarr-labs/homarr/issues/5773 +export const sessionTokenCookieName = `${env.AUTH_COOKIE_PREFIX}.session-token`; export const expireDateAfter = (seconds: number) => { return new Date(Date.now() + seconds * 1000);