Skip to content

EC route in the shared app, verified on Fastly #1598

Description

@aram356

Summary

EC route in the shared app, verified on Fastly: a same-origin POST to /_ts/api/v1/ec mints, records, runs the providers, takes the posted consent and withdrawals, and answers with the mirror's view as JSON; a GET answers Prebid's pixelUrl with a 204 and only re-sets an existing SharedID (reconciliation from #1535).

Part of #1558, server-side identity on every adapter. Plan ID T19, planned in wave 7 (MVP: Fastly and Axum); priority P1, size M. Design: spec; order and dependencies: plan.

Requirements

ID-9 EC route

A same-origin route, /_ts/api/v1/ec by default, lets the script Trusted Server injects into proxied pages report consent and receive identity, so a reader who consents after the page loads gets an EC on that page view instead of the next navigation. A POST whose Origin matches the page mints the EC when the reader has none and consent allows, creates the record, runs the providers as a navigation does, and sets ts-ec, the SharedID cookie and the mirror cookie on its own response. It carries the consent strings the script reads from the CMP, the TCF string from __tcfapi and the GPP string from __gpp (Sourcepoint's capture from #1535 where present), which replace the consent cookies for that request; a withdrawal expires ts-ec, the SharedID cookie and the mirror cookie and tombstones the record in that response. The POST answers with JSON holding exactly what WB-4 would put in the mirror cookie for that request, and the SharedID only under SID-2 and SID-6; the script merges it into Prebid, and nothing else in the record reaches the page. A plain GET answers Prebid's SharedID pixelUrl with an empty 204: it never mints, calls a provider or changes consent, and only re-sets an existing SharedID cookie from the record (WB-3). The script sends consent changes in order with at most one call in flight. While a call is in flight, a newer change replaces a waiting one, except a waiting withdrawal, which is never replaced: later changes wait behind it. So every withdrawal reaches the server, after any grant in progress and before any later grant. The script discards a result that arrives while a newer consent change is waiting, and a withdrawal removes the IDs it merged into Prebid. Every response carries Cache-Control: no-store; a rate limit on the route waits with ID-6 (DEC-15). The route is served from the page's own host, which WebKit's cap on cookies from non-document responses exempts when the IP matches the page's (read from WebKit's source; the Accept tests it).

WB-1 Document and EC route responses only

Identity cookies are set on top-level HTML document responses and on EC route responses (ID-9), on every supported adapter.

WB-3 SharedID cookie

When SID-2 and SID-6 allow it, the server sets the SharedID cookie from the record if the request has none or a different value. When neither the record nor the request has one, the server creates a SharedID in the format Prebid writes (a UUID v4), stores it in the record and sets it the same way, because a browser may block Prebid.js from loading at all (DEC-7). It creates and writes back a SharedID only when the configured storage.type is cookie (Prebid's cookie&html5 counts as cookie): with html5 Prebid never reads the cookie, so a server-made SharedID would split the reader's identity (SID-3). It uses Prebid's storage name and the Domain Prebid writes with, is not HttpOnly, and is Secure; SameSite=Lax for one year. It keeps Prebid's <name>_cst companion consistent, since a missing or different one makes Prebid rewrite the cookie from script, which Safari caps. Prebid's extend with a pixelUrl keeps working alongside it.

WB-4 Mirror cookie

__Host-ts-eids-mirror as in #1489: opt-in per partner (DEC-10), dropped per vendor when TCF consent is denied, needs bidstream_enabled and Purpose 4, 3,584-byte budget, host-only. Every mirrored ID expires within 30 days.

Current state

What the code does today, from the design's survey at the revisions it names:

Acceptance criteria

  • ID-9: On Fastly and Axum, a consented same-origin POST with no ts-ec creates the record and sets ts-ec and the SharedID cookie in that response
  • ID-9: Its JSON omits a non-mirrored partner's ID, an ID whose vendor's TCF consent is denied and an expired ID
  • ID-9: A withdrawal issued while a grant call with no EC and delayed providers is in flight leaves no identity cookie, no live record and no ID in Prebid
  • ID-9: With grant A in flight, a withdrawal B and then a grant C reach the server as A, B, C, so B expires the identity cookies and tombstones A's record and C is handled as a first consent with no ts-ec
  • ID-9: A cross-site Origin, a GET and a client ID-7 refuses mint nothing and call no provider
  • ID-9: An unchanged returning reader gets no Set-Cookie
  • ID-9: In Safari, ts-ec and the SharedID cookie set by the route keep their one-year Max-Age on Fastly and on Axum behind CloudFront
  • WB-1: Identity Set-Cookie headers appear on navigation and EC route responses and never on asset or other API responses
  • WB-3: Record present and cookie absent gives both cookies with those attributes
  • WB-3: A consented first visit with no SharedID anywhere gets one created, stored and set, and a reader with _pubcid_optout gets none
  • WB-3: With storage.type = html5 the server neither creates nor sets a SharedID
  • WB-3: An equal cookie with a consistent <name>_cst gives no Set-Cookie
  • WB-3: An equal cookie with a missing or different <name>_cst re-sets only the companion
  • WB-3: Purpose 1 denied or _pubcid_optout expires both
  • WB-3: Purpose 4 denied with Purpose 1 granted gives no Set-Cookie and leaves the browser's cookie alone
  • WB-4: An ID written with no stated expiry is mirrored with an expiry of at most 30 days

Dependencies

Blocked by:

Blocks:

Related pull requests

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions