DEFERRED_SECURITY.md item 1 still told the calendar to move its session
credentials from query parameters into X-Session-Id/X-Session-Key. That
was the right advice when two other modules read those headers; both
stopped in the cutover, so following it now would build a second
mechanism just as the first is being retired. The fix is the shared
admin identity, which closes the item outright rather than moving the
credential somewhere safer.
Also annotates the one assertion in auth-binding.ts that cannot
currently fail. It is kept deliberately - it is a tripwire against
someone reintroducing a header-session fallback for calendar users who
have not been invited yet, which is the shortcut this whole step exists
to close - but that was worth saying out loud rather than leaving it to
look like an oversight.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feedback.auth.ts and tickets.auth.ts each become one binding to
requireAppAccess. Everything downstream was already written against
requireAdminAuth and res.locals.admin, and both still mean what they
meant, so no router or service changed. What changed is the policy: an
activated @nachklang.art account is no longer sufficient, an explicit
per-app permission is.
Three things followed from that and are not obvious from the diff:
- APP_ORIGINS gets a production default. It feeds better-auth's
trustedOrigins, and this is the first time the tickets and feedback
origins matter there - before, the only browser origin that ever
reached /admin/auth was the admin app itself. An origin missing from
that list fails in a way that is easy to misread: sign-in works, the
app works, and only sign-out returns an origin error.
- Nothing reads X-Session-* any more; these two files were the last
readers, and the calendar module passes its session in query
parameters. The headers stay in the CORS allowedHeaders only so a
browser still running a pre-cutover bundle gets a clean 401 rather
than a preflight failure, and can come out once both frontends are
deployed.
- 40 admin operations documented a required X-Session-Id/X-Session-Key
in swagger. They now declare the AdminSessionCookie scheme the admin
module already defined, and each documents a 403 next to its 401.
The integration assertions flip as their own comment predicted: one
admin cookie opens both /feedback/admin/me and /tickets/admin/me, a
user holding only feedback gets 200 and 403 respectively, and a legacy
header session gets 401. The unit test that covered the old header
authenticator is replaced by one asserting each module is bound to its
own app and that neither consults the calendar users service.
163 unit tests and 43 integration tests green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A production password containing '#' was truncated after the dotenv 8 -> 16
bump (unquoted '#' now starts a comment), causing MariaDB access denied.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Prep PR for the admin auth module (docs/plan-admin-auth.md step 1).
better-auth 1.7 ships ESM only, so the API moves off CommonJS:
- "type": "module", module nodenext, target ES2024, .js suffixes on all
relative imports, require('mariadb'|'cors') replaced by imports, and
export= packages (winston, app-root-path, bcrypt) consumed via default
imports. The logger now uses appRoot.path explicitly.
- TypeScript 5.9, @types/node 26, tslint removed. Node 26 pinned via
engines and .nvmrc (Plesk runs 26).
- Jest 28 + ts-jest replaced by vitest 5. Eight test files depend on
hoisted module mocks with static imports and resetModules + require,
which Jest's ESM mode does not support; vitest keeps them nearly
verbatim. Coverage via @vitest/coverage-v8 (lcov), Sonar generic report
via vitest-sonar-reporter, so sonar-project.properties is unchanged.
vitest.config.ts sets FEEDBACK_IP_SALT so the suite passes without a
local .env.
- dotenv 8 -> 16 and axios 0.24 -> 1.x: their old typings are not
resolvable under nodenext.
- autoCommit: false dropped from the pool configs; it is not a mariadb
connector option and was silently ignored.
tsc clean, 96/96 tests green, compiled app boots and serves /, /docs and
CORS under Node ESM.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>