Skip to content

Logged-in-aware exposed state of cloud.datum.net that can only be read by datum.net #893

Closed

Description

@felixwidjaja

High-Level Summary

Every goal in #890 (nav/CTA personalization, signup-prompt suppression, roadmap voting attribution, form prefill, etc.) needs one prerequisite: a way for datum.net to know whether the current browser is signed in to Datum Cloud (cloud.datum.net) — without exposing that signal to any other origin.

Today that signal doesn't exist. This issue is scoped to just building it — not to any of the personalized UI that consumes it (that stays tracked under #890 and its other sub-issues).

Current state (verified)

  • cloud-portal's session cookie (_session, AUTH_COOKIE_KEYS.SESSION) is set with domain: new URL(env.public.appUrl).hostname (app/utils/auth/auth.service.ts) — i.e. scoped to cloud.datum.net's own hostname, httpOnly, sameSite: lax. It is not shared across .datum.net, so the browser never sends it on a datum.net request. This is the correct, secure default — not a bug to fix in cloud-portal.
  • datum.net's repo already has a stub at src/pages/api/user.ts that tries to read a _session cookie directly and calls the IAM API with it. Because of the point above, this cookie is never actually present on a datum.net request in production — the endpoint is effectively dead code today. It isn't wired into Nav.astro or anywhere else in the UI.

The ask

Design and implement a narrow, purpose-built way for datum.net to learn "is this visitor currently signed in to Datum Cloud" (and probably a minimal display payload — name/org — TBD by design), that:

  • Only datum.net (https://www.datum.net / https://datum.net) can read — not an arbitrary third-party origin
  • Leaks nothing beyond what's needed for personalization (no tokens, no scopes, no permissions)
  • Fails closed: any error, timeout, or ambiguous state renders datum.net as if the visitor is signed out

Candidate approaches to evaluate at design time (not prescribing one):

  • A dedicated endpoint on cloud.datum.net (or auth.datum.net) that responds to a credentials: 'include' cross-origin request from datum.net, with CORS locked to that exact origin, returning a minimal { signedIn: boolean, displayName?, orgName? } shape
  • A short-lived, purpose-scoped signed token/postMessage handshake instead of a raw CORS+cookie read, if the team wants to avoid widening cookie/CORS trust between the two apps

This spans at least cloud-portal (and/or auth-ui) plus datum.net — flagging as cross-repo/backend-required.

Non-Goals

  • Any personalized UI itself (nav CTA swap, signup suppression, roadmap voting, form prefill, etc.) — those are separate, downstream sub-issues of Create a more personalized website experience for users #890
  • Changing where/how cloud-portal's own session cookie is scoped — it should stay app-local; this issue is about a separate, narrowly-scoped signal for datum.net to consume, not widening the existing session cookie's trust boundary

Relationship to #890

Sub-issue of #890 — blocks the "Navigation and primary CTA" and "Suppress signup prompts globally" goals listed there at minimum; several of the other goals (roadmap voting attribution, form prefill) will also need this signal or an extension of it.

Activity

  1. github-actions commented on Sep 23, 2026

    @github-actions

    Issues must have exactly one assignee, but 2 were assigned (@yahyafakhroji, @gaghan430). All assignees have been removed. Please assign a single owner.

  2. yahyafakhroji commented on Sep 24, 2026

    @yahyafakhroji

    @felixwidjaja @AriaEdo

    The portal side is live on staging (cloud-portal#1581, infra#5964). How it works: a datum.net page calls the portal with credentials: 'include', the browser sends the portal cookie on its own, and the portal only answers our website origins.

    const res = await fetch(`${PORTAL}/api/website/session-status`, { credentials: 'include' });
    const status = res.ok ? await res.json() : { signedIn: false };

    Routes:

    • GET /api/website/session-status: signed in or not, plus name, org, dashboard link
    • GET /api/website/profile: email and the HelpScout signature
    • POST /api/website/feedback: title and body from a signed-in visitor
    • GET /api/website/login?returnTo=<page>: sign-in link that returns the visitor to the page

    Keep in mind: anything other than a 200 means signed out, don't store the email in the browser, and new origins (previews too) need adding to WEBSITE_ORIGINS in infra first.

  3. felixwidjaja commented on Sep 24, 2026

    @felixwidjaja
    MemberAuthor

    Thanks guys! It works.

    Image
  4. jacobsmith928 commented on Sep 24, 2026

    @jacobsmith928
    Member

    @felixwidjaja please coordinate a brief changelog with @kaleygel thanks!

  5. felixwidjaja commented on Sep 25, 2026

    @felixwidjaja
    MemberAuthor

    @yahyafakhroji ; as we discussed. We'll need to expand this. Can you provide the marked (Y):

    All projects || Create new project (Y)
    Account settings
    Logout (Y)

    Thank you.

  6. felixwidjaja commented on Sep 25, 2026

    @felixwidjaja
    MemberAuthor
  7. felixwidjaja commented on Sep 28, 2026

    @felixwidjaja
    MemberAuthor

    Just in:

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

Priority

None yet

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions