fix: prevent caching draft posts #306
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
This PR fixes an issue where authenticated user data could be cached and subsequently served to public (unauthenticated) users.
The Problem
When a logged-in user makes a GraphQL request without a nonce (e.g., via a direct URL like
/graphql?query={posts(where:{status:DRAFT}){nodes{title}}}), the following sequence occurs:AppContext->vieweris set to the authenticated user (e.g., admin with ID 123)has_authentication_errors()runs: Since no nonce was provided, WPGraphQL core callswp_set_current_user(0)is_user_logged_in()to determine if caching is enabledAt step 4,
is_user_logged_in()returnsfalsebecausewp_set_current_user(0)was called in step 3. However, the query results from step 2 still contain authenticated data. This caused Smart Cache to incorrectly cache the authenticated response, which could then be served to public users.The Solution
Instead of using
is_user_logged_in()(which can change mid-request), we now useAppContext->viewerto determine the user's authentication state:AppContext->vieweris set once at Request creation and never changeswp_set_current_user(0)is called,AppContext->viewerstill reflects the original authenticated userChanges
src/Cache/Query.php$requestproperty to accessAppContext->viewerbuild_key()to useAppContext->viewer->IDinstead ofwp_get_current_user()->IDsrc/Cache/Results.phpis_object_cache_enabled()to checkAppContext->viewer->exists()instead ofis_user_logged_in()is_object_cache_enabledresult per-request to ensure consistent behaviorCache-Control: no-storeheader for authenticated requests to prevent network/CDN cachingsrc/Cache/Collection.php$this->requestbefore callingbuild_key()so it can accessAppContext->viewerTest Coverage
Added comprehensive test coverage:
tests/wpunit/AuthenticatedRequestCacheTest.php- Unit tests covering:AppContext->viewerexists (authenticated)Cache-Control: no-storeheader is set for authenticated requeststests/acceptance/AuthenticatedRequestCacheCest.php- Acceptance tests covering:How to Test
/graphql?query={posts(where:{status:DRAFT}){nodes{title status}}}Related
This fix addresses a quirk in WPGraphQL core where
wp_set_current_user(0)is called inafter_execute()— after the query has already executed with authenticated permissions. A separate issue will be opened in WPGraphQL core to address the timing of this authentication check.Note
Uses AppContext->viewer to disable caching for authenticated requests (and in cache keys), adds Cache-Control: no-store, and introduces tests to prevent draft/private data leaking to public users; updates CI and test configs.
Results: Determine auth viaAppContext->viewer(notis_user_logged_in()), cache the decision per-request, and setCache-Control: no-storefor authenticated requests; keep auth requests out of object cache.Query: Add$requestand useAppContext->viewer->IDinbuild_key()to segregate by user reliably.Collection: Set$this->requestbeforebuild_key()so cache keys include stable user context.wpunit/AuthenticatedRequestCacheTest.phpcovering auth vs public caching, header behavior, and sequential auth-state scenarios.AuthenticatedRequestCacheCest.phpverifying no leak of draft posts, public caching works, and auth bypasses cache.tests/acceptance.suite.ymlto useWPDb; add helper and config tweaks.actions/checkout@v4,actions/cache@v4,[email protected]) and fixGITHUB_OUTPUTusage.codeception.dist.yml) and composer dev setup/scripts; keep lockfile in sync.Written by Cursor Bugbot for commit 7d8f951. This will update automatically on new commits. Configure here.