Skip to content

Support custom fallback token resolver for third-party contexts #578

@Startouf

Description

@Startouf

Current library capabilities do not cover embedding app code in third-party websites

Scenario

  • api.my-company.com --> serving search results
  • www.my-customer.com --> loading some initial JS code (like a searchbar) from api.my-company.com/form
  • www.my-customer.com --> .... and dynamically querying api.my-company.com/results

Cookies are increasingly blocked due to:

Safari ITP (Intelligent Tracking Prevention)
Chrome's third-party cookie deprecation
Firefox Enhanced Tracking Protection

In these contexts, Ahoy's current token resolution chain fails to identify visitors:

Ahoy-Visitor/Ahoy-Visit headers → not available / (or we cannot have both tokens)
ahoy_visitor/ahoy_visit cookies → blocked by browser
visitor_token/visit_token params → only when Ahoy.api = true
Generate new UUID → creates a new visitor on every request

This means widget traffic generates a new visitor/visit on every request, making analytics useless.

Current workaround

We implemented a monkeypatch using prepend to inject a fallback token (that we already generate differently) from the controller as token:

module AhoyTrackerFallbackExtension
  def existing_visitor_token
    super || controller&.try(:fallback_visitor_id)
  end

  def existing_visit_token
    super || controller&.try(:fallback_visitor_id)
  end
end

Ahoy::Tracker.prepend(AhoyTrackerFallbackExtension)

Our controller stores a session-based ID (from widget SessionStorage) in a fallback_visitor_id field submitted to our API, which Ahoy then uses as a last resort when cookies are unavailable.

Proposed solution
Add a configuration option for custom token resolvers, e.g.:

# Option A: Callback-based
Ahoy.visitor_token_resolver = ->(controller, request) {
  controller&.try(:fallback_visitor_id)
}

Use case
Cross-domain tracking where cookies don't work
Any context where the client can provide a stable session ID via params/headers but cookies are blocked, and this session ID is already used in another context than Ahoy (we want to avoid sending them twice in 2 different headers/params)

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