Skip to content

Moved gift-link verification into the posts/pages Content API#29078

Draft
kevinansfield wants to merge 1 commit into
mainfrom
gift-links-content-api-option
Draft

Moved gift-link verification into the posts/pages Content API#29078
kevinansfield wants to merge 1 commit into
mainfrom
gift-links-content-api-option

Conversation

@kevinansfield

Copy link
Copy Markdown
Member

ref #29064

Draft demonstrating an alternative to the public GET /content/gift_links/:token endpoint proposed in #29064 — not intended to merge as-is until we've settled on a direction.

The entry controller currently verifies gift tokens itself and re-reads the entry as a paid-member shim. That gives the frontend gift-service knowledge through the proxy seam (proxy.giftLinks, proxy.createPaidMemberShim), costs a second entry lookup on every gift view, and any future consumer of gift access has to reimplement the same verification dance.

This moves verification inside the posts/pages Content API read, where content gating already lives:

  • The frontend passes the raw ?gift token through the single entry lookup as read context — pure URL knowledge, no service access. Its remaining gift handling is reactive: an unverifiable token rejects the lookup with INVALID_GIFT_TOKEN, which triggers the existing strip-?gift-and-301; a verified render gets the noindex/no-referrer headers and toast/analytics flag.
  • The read endpoints resolve the token, verify it against the entry actually being read (so a token for one post can never unlock another), and unlock gated content via the same paid-member shim previews use.
  • Resolution happens in generateCacheKeyData, before the response cache is consulted, mirroring how member auth is resolved per-request ahead of the cache: unlocked variants are cached by resolved post id, revoking or resetting a link takes effect immediately, and an unresolvable token can never share a cache key with a plain read — a cache hit can never serve a gated 200 where a miss would 403.
  • ?gift stays intentionally a site-frontend concern: the token travels as internal read context only, so the public Content API ignores the param entirely (pinned by a canary test).

The existing test/e2e-frontend/gift-links.test.ts suite passes unchanged.

ref #29064

- the entry controller previously verified gift tokens itself and re-read the
  entry as a paid-member shim, giving the frontend gift-service knowledge
  through the proxy seam and costing a second lookup per gift view
- the frontend now passes the raw ?gift token through the single entry lookup
  as read context; the posts/pages read endpoints resolve it, verify it
  against the entry being read and unlock its gated content via the same shim
  previews use, or reject with INVALID_GIFT_TOKEN for the frontend to
  strip-and-301
- the token resolves to its post id in generateCacheKeyData, before the
  response cache is consulted: unlocked variants are cached by resolved post
  id, revocations take effect immediately, and an unresolvable token can
  never share a cache key with a plain read
- ?gift stays intentionally a site-frontend concern: the token travels as
  internal read context only, so the public Content API ignores the param
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 12072922-fa13-4615-b72d-697ffdd67c8e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gift-links-content-api-option

Comment @coderabbitai help to get the list of available commands.

@kevinansfield kevinansfield requested review from jonatansberg and rob-ghost and removed request for rob-ghost July 3, 2026 15:18
@nx-cloud

nx-cloud Bot commented Jul 3, 2026

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit 31661de

Command Status Duration Result
nx run ghost:test:ci:integration ✅ Succeeded 2m 35s View ↗
nx run ghost:test:integration ✅ Succeeded 2m 42s View ↗
nx run ghost:test:legacy ✅ Succeeded 2m 53s View ↗
nx run ghost:test:e2e ✅ Succeeded 2m 28s View ↗
nx run-many --target=build --projects=tag:publi... ✅ Succeeded <1s View ↗
nx run-many -t lint -p ghost ✅ Succeeded 33s View ↗
nx run-many -t test:unit -p ghost ✅ Succeeded 28s View ↗
nx run @tryghost/admin:build ✅ Succeeded 7s View ↗
Additional runs (2) ✅ Succeeded ... View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-07-03 15:28:03 UTC

@rob-ghost rob-ghost 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.

👍🏻 best approach yet.

Not blocking but we could add a test for gift link rendering of a post which contains a kg-gated-block like:

<!--kg-gated-block:begin nonMember:true-->
<p>Anonymous gated content</p>
<!--kg-gated-block:end-->

which would enshrine the need to have a member shim when rendering a post with a gift link. This is currently missing,

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants