Predictable AUTH_SECRET Enables Forged NextAuth Sessions
Severity: High when a public placeholder is configured
CWE: CWE-798 (Use of Hardcoded Credentials), CWE-321 (Use of Hard-coded Cryptographic Key)
Affected version: 0.12.4
Affected revision: f5b9550096d6e6190dd12a82af487adcea66be06
Summary
TaskTrove uses NextAuth JWT sessions but does not require a strong, unique AUTH_SECRET.
The authentication configuration falls back to this hardcoded value:
The self-hosting Pro Compose template configures authentication with another public value:
When AUTH_SECRET=CHANGE_ME is used, an attacker who knows the public value can generate a valid NextAuth JWT session and access routes protected by withAuthentication without the configured password.
When AUTH_SECRET is completely unset, the application instead disables authentication checks. That is a separate fail-open deployment mode described below.
Evidence
Predictable NextAuth session secret
apps/web/auth.ts#L65-L84 enables JWT sessions and configures the fallback:
session: {
strategy: "jwt",
},
// when AUTH_SECRET is not set, disable auth
secret: process.env.AUTH_SECRET || "auth-disabled",
The same file assigns the authenticated user a fixed ID of "1" in the credentials provider at #L37-L52.
The self-hosting template exposes a public authentication secret:
Protected route consumption
apps/web/lib/middleware/auth.ts#L64-L104 calls auth() and accepts the request when a session with session.user exists.
This wrapper protects state-changing and data-reading routes, including:
The application’s own tests confirm that AUTH_SECRET is optional and that missing configuration bypasses authentication:
Deployment conditions
There are two relevant configurations:
AUTH_SECRET=CHANGE_ME: authentication is enabled, but the NextAuth JWT secret is publicly known. Forged session cookies can be accepted.
AUTH_SECRET unset: isAuthEnabled() returns false, and withAuthentication() calls the protected handler directly. The default self-hosting Compose file leaves AUTH_SECRET commented out at selfhost/docker-compose.yml#L7-L10.
The first configuration is a token-forgery issue. The second configuration is an authentication-disabled deployment mode. Both are unsafe if the service is exposed beyond a trusted local user.
Proof of concept
Run this only against a local TaskTrove instance. The PoC uses NextAuth’s own JWT encoder and does not contact or modify any remote system.
// poc.mts
import { encode } from "next-auth/jwt"
const secret = process.env.AUTH_SECRET || "CHANGE_ME"
const salt = process.env.AUTH_COOKIE_NAME || "authjs.session-token"
const token = await encode({
secret,
salt,
token: {
sub: "1",
id: "1",
name: "forged-user",
},
})
console.log(token)
# Run from the TaskTrove web workspace with its dependencies installed.
AUTH_SECRET=CHANGE_ME pnpm exec tsx poc.mts
# Use the generated value as the session cookie on a local instance.
curl -i http://127.0.0.1:3000/api/v1/tasks \
-H "Cookie: authjs.session-token=${FORGED_TOKEN}"
Expected result: with AUTH_SECRET=CHANGE_ME, NextAuth can decrypt the attacker-generated session, auth() returns a session containing user ID 1, and withAuthentication() allows the request to reach the protected route.
For HTTPS deployments, use the deployment’s secure cookie name, commonly __Secure-authjs.session-token, as the AUTH_COOKIE_NAME/salt value and send that cookie name in the request.
Impact
An unauthenticated attacker who knows the configured public placeholder can forge a valid NextAuth session. The attacker can then access protected TaskTrove API routes and perform actions as the application’s single user, including creating, changing, or deleting task data, depending on the enabled route set.
If AUTH_SECRET is omitted entirely, the application’s authentication middleware is bypassed by design. Any network-accessible deployment in that mode exposes routes intended to be protected by withAuthentication.
Remediation
- Remove
auth-disabled and CHANGE_ME as authentication secrets.
- Require
AUTH_SECRET at startup and fail closed when it is missing, empty, or a known placeholder.
- Generate a unique random value, for example
openssl rand -base64 32, and provide it through a secret manager or protected deployment environment.
- Do not use “authentication disabled” as the default for an exposed deployment. If local unauthenticated mode is required, require an explicit opt-in flag and bind it to loopback by default.
- Rotate
AUTH_SECRET on affected deployments and invalidate existing sessions after rotation.
- Add a regression test proving that production startup fails without a strong
AUTH_SECRET and that a token signed with a known placeholder is rejected.
Disclosure note
I could not find a repository-specific SECURITY.md or a visible private vulnerability-reporting channel. For that reason, this report is provided in Issue-compatible format as a fallback.
This report is part of my ongoing research into the security of authentication mechanisms. If you have any questions about this finding or the evidence provided, please feel free to mention me in the discussion or contact me directly at any time. I would be very happy to discuss it and to contribute, in any way I can, to improving the security of TaskTrove.
References
Predictable AUTH_SECRET Enables Forged NextAuth Sessions
Severity: High when a public placeholder is configured
CWE: CWE-798 (Use of Hardcoded Credentials), CWE-321 (Use of Hard-coded Cryptographic Key)
Affected version:
0.12.4Affected revision:
f5b9550096d6e6190dd12a82af487adcea66be06Summary
TaskTrove uses NextAuth JWT sessions but does not require a strong, unique
AUTH_SECRET.The authentication configuration falls back to this hardcoded value:
The self-hosting Pro Compose template configures authentication with another public value:
When
AUTH_SECRET=CHANGE_MEis used, an attacker who knows the public value can generate a valid NextAuth JWT session and access routes protected bywithAuthenticationwithout the configured password.When
AUTH_SECRETis completely unset, the application instead disables authentication checks. That is a separate fail-open deployment mode described below.Evidence
Predictable NextAuth session secret
apps/web/auth.ts#L65-L84enables JWT sessions and configures the fallback:The same file assigns the authenticated user a fixed ID of
"1"in the credentials provider at#L37-L52.The self-hosting template exposes a public authentication secret:
selfhost/docker-compose-pro.yml#L9-L10setsAUTH_SECRET=CHANGE_ME.apps/web/package.json#L1-L10identifies the affected web application as version0.12.4and uses NextAuth5.0.0-beta.30.Protected route consumption
apps/web/lib/middleware/auth.ts#L64-L104callsauth()and accepts the request when a session withsession.userexists.This wrapper protects state-changing and data-reading routes, including:
apps/web/app/api/v1/tasks/route.ts#L93-L106apps/web/app/api/v1/projects/route.ts#L95-L108apps/web/app/api/backup/route.ts#L32-L39The application’s own tests confirm that
AUTH_SECRETis optional and that missing configuration bypasses authentication:apps/web/auth.test.ts#L134-L138expects the fallbackauth-disabled.apps/web/lib/middleware/auth.test.ts#L355-L402expects protected handlers to run without authentication whenAUTH_SECRETis absent.Deployment conditions
There are two relevant configurations:
AUTH_SECRET=CHANGE_ME: authentication is enabled, but the NextAuth JWT secret is publicly known. Forged session cookies can be accepted.AUTH_SECRETunset:isAuthEnabled()returnsfalse, andwithAuthentication()calls the protected handler directly. The default self-hosting Compose file leavesAUTH_SECRETcommented out atselfhost/docker-compose.yml#L7-L10.The first configuration is a token-forgery issue. The second configuration is an authentication-disabled deployment mode. Both are unsafe if the service is exposed beyond a trusted local user.
Proof of concept
Run this only against a local TaskTrove instance. The PoC uses NextAuth’s own JWT encoder and does not contact or modify any remote system.
Expected result: with
AUTH_SECRET=CHANGE_ME, NextAuth can decrypt the attacker-generated session,auth()returns a session containing user ID1, andwithAuthentication()allows the request to reach the protected route.For HTTPS deployments, use the deployment’s secure cookie name, commonly
__Secure-authjs.session-token, as theAUTH_COOKIE_NAME/salt value and send that cookie name in the request.Impact
An unauthenticated attacker who knows the configured public placeholder can forge a valid NextAuth session. The attacker can then access protected TaskTrove API routes and perform actions as the application’s single user, including creating, changing, or deleting task data, depending on the enabled route set.
If
AUTH_SECRETis omitted entirely, the application’s authentication middleware is bypassed by design. Any network-accessible deployment in that mode exposes routes intended to be protected bywithAuthentication.Remediation
auth-disabledandCHANGE_MEas authentication secrets.AUTH_SECRETat startup and fail closed when it is missing, empty, or a known placeholder.openssl rand -base64 32, and provide it through a secret manager or protected deployment environment.AUTH_SECRETon affected deployments and invalidate existing sessions after rotation.AUTH_SECRETand that a token signed with a known placeholder is rejected.Disclosure note
I could not find a repository-specific
SECURITY.mdor a visible private vulnerability-reporting channel. For that reason, this report is provided in Issue-compatible format as a fallback.This report is part of my ongoing research into the security of authentication mechanisms. If you have any questions about this finding or the evidence provided, please feel free to mention me in the discussion or contact me directly at any time. I would be very happy to discuss it and to contribute, in any way I can, to improving the security of TaskTrove.
References