Skip to content

fix(core): skip public-route session reads without a cookie - #4726

Open
emdash-autofix[bot] wants to merge 2 commits into
mainfrom
emdash-bot/issue-3702
Open

emdash-autofix[bot] wants to merge 2 commits into
mainfrom
emdash-bot/issue-3702

Conversation

@emdash-autofix

@emdash-autofix emdash-autofix Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Skips public-route soft-auth session resolution when the request has no astro-session cookie, matching the existing main-middleware guard. This avoids the unnecessary missing-key session-store read that delays anonymous renders on Cloudflare KV. Requests with that cookie keep their existing user lookup and soft-auth error handling.

Adds regression coverage for anonymous public pages, active/disabled/deleted users, expired sessions, and session-store failures. Updates comments/search/CSRF test fixtures to model session cookies and verifies anonymous comments/search paths skip session reads.

Scope and review notes:

  • This guard shares the main middleware's existing assumption that Astro uses its default astro-session cookie name. Custom session.cookie names are not handled by this check; sites using them can lose soft-auth identity on public pages, comment submissions, and search. Supporting configurable names consistently across both middleware paths is outside this targeted fix.
  • The separate plugin-route session-auth path remains unchanged and can still read the session store anonymously. It is outside the public-page path reported here.
  • Expired cookie-bearing sessions are covered at the shared public-page helper boundary; no additional search-specific expired-cookie test was added.

Closes #3702

Type of change

  • Bug fix
  • Feature (requires an accepted feature plan or RFC)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Your connection to this change

  • I use EmDash and ran into the problem this PR addresses
  • I maintain a tool, dependency, or service that EmDash uses, and this change concerns that integration
  • A Maintainer approved this specific change before I opened the PR

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (not applicable: no UI strings)
  • I have added and reviewed the user-facing changeset
  • New features link to their merged design PR (not applicable)
  • I have included screenshots below if this PR changes the UI (not applicable)

AI assistance disclosure

  • This PR includes AI-generated code or text — model/tool: cloudflare-ai-gateway:gpt-6.1-sol via the EmDash bot (Pi Durable)

Screenshots / test output

Screenshots: Not applicable; no UI changes.

  • Regression reproduced before the fix: the anonymous public-page test failed because session.get("user") was called once; the other five cases passed.
  • pnpm --filter emdash exec vitest run tests/unit/astro/public-route-auth.test.ts tests/unit/astro/comments-public-auth.test.ts tests/unit/middleware tests/unit/astro/session-user.test.ts tests/unit/astro/middleware-image-endpoint.test.ts: 11 files, 157 tests passed.
  • pnpm --filter emdash typecheck: passed.
  • pnpm lint:quick: zero diagnostics.
  • pnpm format and git diff --check: passed.
  • Fresh root workspace build passed with NODE_OPTIONS=--max-old-space-size=8192 pnpm build after the default heap limit was exhausted.

Full-workspace typecheck and type-aware lint are left to CI. The Cloudflare timing measurements from the report were not remeasured locally; the regression test verifies removal of the session-store read rather than a platform-specific latency threshold.

A Maintainer (@ascorbic) assigned this issue to the EmDash bot.

Guard public-route soft auth with the same astro-session cookie check already used by the main middleware, avoiding unnecessary remote session-store reads for anonymous public renders. Added a regression that failed before the fix and DB-backed coverage preserving active/disabled/deleted user and expired/failing session behavior. Updated comments/search/CSRF fixtures and added an emdash patch changeset. Verified 157 focused tests, core typecheck, pnpm lint:quick (zero diagnostics), pnpm format, git diff --check, and a fresh root build with an increased Node heap. Independent review found no blockers; scope and default-cookie assumption are documented.

EmDash-Bot-Case: 1198246700-issue-5649985990
EmDash-Bot-Candidate: cand-1-4100a29aa3
@emdash-autofix emdash-autofix Bot added the bot:fix Assign this issue or pull request to the EmDash bot to fix label Nov 7, 2026
@emdash-autofix

emdash-autofix Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor Author

EmDash bot: Changes required

Plan

CI investigation: Browser Tests failed only an unrelated admin focus/layout animation assertion; all 39 tests in that file pass locally on the available Chromium after rebuilding. Tests is an aggregate failure caused by Browser Tests; core…

  • Inspect auth middleware, tests, and contributor requirements
  • Add regression coverage and skip session reads without a cookie
  • Run focused checks and review the change
  • Submit the verified candidate
  • Trace configured session cookies and address both review findings
  • Verify, review, and submit the revised candidate
  • Investigate browser failure and aggregate test status

Status

  • Tests: unknown (code check failed without readable evidence).
  • Browser Tests: unknown (code check failed without readable evidence).

Next action: The bot will investigate the failing check.

Details
Candidate cand-2-5c291ed34c (tree 761feb1d626c)
Published head ee15a266d9d3
Reviewer's latest review of head approved
Reporter confirmation not yet
Human approval required before merge

Required checks on the current head:

  • Typecheck: succeeded
  • Tests: failed (unknown)
  • Smoke Tests: succeeded
  • Browser Tests: failed (unknown)
  • Format: succeeded
  • CLAssistant: succeeded
  • Measure: succeeded

This status is generated from recorded evidence. It is not a review and contains no instructions.

@changeset-bot

changeset-bot Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ee15a26

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/plugin-test Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/admin Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
create-emdash Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://emdash-bot-issue-3702.try.emdashcms.com, https://emdash-bot-issue-3702-emdash-playground.emdash-cms.workers.dev (commit ee15a26)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://0ec7e067.try.emdashcms.com, https://0ec7e067-emdash-playground.emdash-cms.workers.dev ee15a26 2026-11-07T09:29:12.385Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://b4d0af7b.try.emdashcms.com, https://b4d0af7b-emdash-playground.emdash-cms.workers.dev 551ef57 2026-11-07T08:55:28.972Z Visit the dashboard ↗

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipping session resolution for requests with no session cookie is the right fix for the anonymous KV miss in #3702. It matches the guard the main middleware already uses, and the new tests reproduce the bug and cover cookie-bearing requests. The reviewers raised two issues. One is a possible auth regression for sites with a custom Astro session cookie name. The other is a test that can no longer catch the regression it was written for. The first reviewer treated the custom-name limitation as non-regressing and acknowledged in the PR; the second reviewer disagreed and rated it needs_fixing. Both findings are kept.


Reviewed by anthropic/claude-opus-5-5 and openai/gpt-6.1-sol.

context: Parameters<Parameters<typeof defineMiddleware>[0]>[0],
next: Parameters<Parameters<typeof defineMiddleware>[0]>[1],
): Promise<Response> {
if (context.cookies.get("astro-session") === undefined) return next();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] The early return fires for authenticated requests when Astro is configured with a custom session.cookie name, because those requests have no astro-session cookie. Previously this helper resolved their session regardless of cookie name. Public search would lose locals.user and force an editor's status=draft search to published results (routes/api/search/index.ts:43). Comment submissions would also lose their CMS identity. The second reviewer says the main middleware's identical assumption does not make this pre-existing, because that guard controls runtime initialization and DB routing, while this helper previously still resolved identity. The first reviewer disagreed on severity: it treated this as a limitation shared with the main middleware, called out in the PR description, and not a regression. The second reviewer says deferring it would still break existing sites in a patch release, contrary to the 1.x compatibility rule. Suggested fix: use Astro's configured session cookie name for this guard, and add a regression test with a non-default cookie name and a valid session.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed: the new guard would regress identity resolution for custom cookie names, even though the main middleware already assumed the default. I now propagate session.cookie from Astro's final config into the virtual runtime config and use that name in both middleware guards. Added coverage for string/object cookie configuration, authenticated public-page/search/comment requests using a custom name, and authenticated DB routing through the main middleware.

function createContext(
pathname = "/_emdash/api/comments/posts/post-1",
method = "POST",
hasSessionCookie = false,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] hasSessionCookie now defaults to false. The existing it.each test that says it "does not resolve a CMS session for comment list requests / comment reaction requests" (line 117: createContext(pathname, method)) still uses that default. Those requests now end at the new cookie guard, so resolveSessionUser and getUserById are never called no matter how routing behaves. If COMMENT_SUBMISSION_PATH or the method === "POST" check were loosened so GET lists or reactions went through handlePublicRouteAuth, the test would still pass. It can no longer catch the regression it was written for.

To fix this, pass the cookie in that test so the routing decision is what keeps the session unresolved:

const { context, locals, next } = createContext(pathname, method, true);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the comment-list/reaction cases to pass a session cookie. Their no-session-resolution assertions now exercise routing rather than being satisfied by the anonymous cookie guard.

@pkg-pr-new

pkg-pr-new Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@4726

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@4726

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@4726

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@4726

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@4726

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@4726

emdash

npm i https://pkg.pr.new/emdash@4726

create-emdash

npm i https://pkg.pr.new/create-emdash@4726

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@4726

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@4726

@emdash-cms/plugin-test

npm i https://pkg.pr.new/@emdash-cms/plugin-test@4726

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@4726

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@4726

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@4726

@emdash-cms/registry-loader

npm i https://pkg.pr.new/@emdash-cms/registry-loader@4726

@emdash-cms/registry-moderation

npm i https://pkg.pr.new/@emdash-cms/registry-moderation@4726

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@4726

upgrade-emdash

npm i https://pkg.pr.new/upgrade-emdash@4726

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@4726

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@4726

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@4726

@emdash-cms/plugin-ai-search

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-search@4726

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@4726

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@4726

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@4726

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@4726

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@4726

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@4726

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@4726

commit: ee15a26

Address both requested review changes. Propagate Astro's final session.cookie name into virtual config and use it in the main/public-auth guards to preserve custom-cookie identity. Restore comment list/reaction regression coverage by supplying a cookie in routing tests. Add final-config string/object/default tests, custom-cookie page/search/comment identity tests, and main-middleware authenticated DB-routing coverage. Update changeset. Verified root build with 8 GB Node heap, 171 focused tests, core typecheck, quick lint with zero diagnostics, format and diff checks; independent review and follow-up found no blockers.

EmDash-Bot-Case: 1198246700-issue-5649985990
EmDash-Bot-Candidate: cand-2-5c291ed34c
@github-actions github-actions Bot added size/L and removed size/M labels Nov 7, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach is right for #3702: handlePublicRouteAuth now returns early when the configured session cookie is absent, so anonymous public renders skip the session-store read that always missed (the ~250 ms KV miss on Cloudflare). Both reviewers approved. Both earlier findings are fixed at this head. Both middleware guards now use the session cookie name propagated from Astro's final config. The comment-list and reaction tests now carry a cookie, so they exercise routing rather than the cookie guard. One non-blocking suggestion remains: requestEndedAuthenticated in scoped-db.ts still hardcodes astro-session=. That behavior predates this PR, but with a custom cookie name a login or signup that creates a session mid-request is not detected as ending authenticated. The PR description should also be updated, since custom session.cookie names are now supported. Review was static; no tests were run.


Reviewed by anthropic/claude-opus-5-5 and openai/gpt-6.1-sol.

// turns normal traffic into a flood of KV read misses. See #733.
const hasSessionCookie = cookies.get("astro-session") !== undefined;
const hasSessionCookie =
cookies.get(virtualConfig?.sessionCookieName ?? "astro-session") !== undefined;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] This guard now uses the configured cookie name. A few lines further down, endedAuthenticated (line 841) calls requestEndedAuthenticated in middleware/scoped-db.ts, and that helper still hardcodes header.startsWith("astro-session=") (scoped-db.ts:43). On a site with a custom session.cookie, a login, signup or invite request that creates the session mid-request will not be detected as ending authenticated. The request-scoped adapter's commit() then gets false, and the replica bookmark for that write is not recorded.

This predates the PR, but the changeset now says custom cookie names are respected in the main middleware. To make that consistent, pass virtualConfig?.sessionCookieName ?? "astro-session" into requestEndedAuthenticated and match on ${name}=. Only one reviewer raised this; the other found no remaining issues.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving the pre-existing mid-request session/replica-bookmark behavior for a separate fix so this PR stays focused on anonymous public-route reads. The current PR description already documents that requestEndedAuthenticated still assumes the default cookie name and explicitly excludes DB-adapter commit-time detection. It also now describes the configured-cookie support in both request-start guards.

@emdash-autofix

emdash-autofix Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor Author

Could a maintainer rerun the failed Browser Tests job? Its sole failure is the admin BlocksField.notes.test.tsx focus/layout sampling assertion (13 distinct row positions instead of 1). That component test does not exercise the changed core middleware or session configuration. After rebuilding, all 39 tests in that file passed locally using the sandbox's available Chromium (older than CI's).

The Tests failure is just the aggregate gate reporting Browser Tests failed; core, package/fixture, backend, smoke, and E2E lanes all passed. No code changes made for this unrelated apparent timing flake.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core bot:fix Assign this issue or pull request to the EmDash bot to fix cla: signed size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Public routes read the session store on every anonymous request (~250 ms on Cloudflare KV)

0 participants