Skip to main content

CMP Portal — Dev Guide

Last updated: 2025-09-21 (Consolidated under docs/cmp)

A minimal admin portal for managing CMP Sites and publishing live configurations. Uses shared React auth hooks (@digiwedge/hooks-auth-web) against the IDP and stores tokens in @digiwedge/global-state. The UI is wrapped in a single providers component (theme + RTL + toasts), with a nested route shell for a consistent admin-grade UX.

Environment

  • VITE_REGISTRY_URL — points to the Registry API base (/api).
    • Recommended for local dev: http://localhost:3318/api (matches the Registry app default).
    • Note: the portal code has a fallback of http://localhost:3410 if this var is not set. Always set VITE_REGISTRY_URL explicitly to avoid surprises.
  • VITE_CDN_CMP_URL (optional; CDN URL for the drop‑in bundle to populate snippet + SRI lookup)
  • VITE_SITE_KEY (default DEV_SITE_KEY)
  • Auth (IDP hooks):
    • VITE_IDP_BASE_URL — base URL for IDP REST API, e.g. http://localhost:3101
    • Add to index.html head: <meta name="idp-audience" content="cmp-portal" /> (audience is sent as X-Client-Audience during login/refresh)
    • Tokens persist in storage under keys: accessToken, refreshToken, jti. useRehydrateTokens() reads these on app start.
    • Dev bypass option (for local only): set VITE_BYPASS_AUTH=1 and set the Registry env DEV_ADMIN_BEARER to a static value. The Portal will gate routes by the bypass flag and the Registry will accept that static bearer on /api/admin/* when not in production.

Create apps/cmp/portal/.env.local:

VITE_REGISTRY_URL=http://localhost:3318/api
VITE_CDN_CMP_URL=http://localhost:8080/dw-cmp.min.js
VITE_SITE_KEY=DEV_SITE_KEY

# IDP login (hooks)
VITE_IDP_BASE_URL=http://localhost:3101

# Dev bypass (local only)
VITE_BYPASS_AUTH=1
# If using static dev bearer, configure Registry's DEV_ADMIN_BEARER to match

Run

pnpm nx serve cmp-portal

Features

  • Login with IDP (PKCE). Requires the cmp.admin scope/role.

    • Uses @digiwedge/hooks-auth-web for login, refresh, logout, and optional MFA. Tokens are persisted by @digiwedge/global-state and rehydrated on load.
    • Routes: /login and /signup render shared AntD pages from @digiwedge/ui-antd.
    • The header Login button navigates to /login; Logout uses the hook’s useLogout().
  • Shell and navigation

    • Nested routes under an AppShell with header + sider; header shows environment badge, theme switch, language switch (LTR/RTL), and a debounced Site Select (remote search).
    • Public Home loads first (//home). Protected routes live under <Protected><AppShell/></Protected>.
    • Lightweight route progress bar appears during navigation.
    • Status pages: /403 (Forbidden), catch-all /404 (NotFound); 500s are handled by an ErrorBoundary.
  • Sites

    • URL-backed search/pagination; skeleton and empty states.
    • “Create Site” drawer with inline validation (key/name/domain), optimistic success: newly created site is selected in the header.
    • Drop‑in snippet copier, verification commands, key rotation, and overrides drawer.
  • Analytics

    • Tabs: Overview, Cookies, Vendors, Artifacts, Audit.
    • /analytics now renders a dashboard:
      • Consent funnel stacked area chart (7/30/90d or custom range) with CSV export and filters for site, region, and bucket (day/hour).
      • Experiment summary bars (variant sessions, accept rate %, p50/p95 decision latency) with CSV export and variant selector.
      • Filters are URL-backed; charts skeleton‑load, handle empty states, and respect registry availability.
    • Diagnostics adds:
      • Consent Mode v2 checklist + simulator
      • Consent Coverage (7d) card (TCF/GPP string coverage, daily)
      • Experiments card (A/B demo results; views, accept%, adjust%)
      • Auth & access diagnostics now surface IDP status (IDLE/OK/MFA/FAIL) and an Access Control latency badge once capability checks run.
  • Config

    • List view with URL‑backed search/pagination.
    • Upsert (Create/Edit) drawer with inline validation and optimistic updates; Delete with confirmation.
    • Editor remains available at /config/edit when needed for advanced use.
  • Sites page lists Sites with actions:

    • Copy snippet — copies the drop‑in script tag with data-site-key and data-config-url; includes SRI from ${CDN}.sri when available.
    • Copy verify cmds — copies a set of curl commands to check security headers and cookies, plus a one‑liner to run the scanner with CMP_REGISTRY_URL for classification.
    • Rotate key — rotates the site key; you must update installed snippets.
    • The last selected Site is remembered in localStorage under cmp.portal.lastSiteKey.
  • Consent analytics widget — per site, last 7/30 days: total, accept all, reject all, partial, and GPC %.

  • Export consents — CSV/JSON/JSONL export by date range with optional filters (region, GPC).

  • Manage overrides — drawer to search/list/paginate overrides; CSV export/import; scope filters (global/site).

  • Reclassify — classify a host and save/remove overrides (site-aware).

  • Vendors helper — add/remove hosts with categories and optional src; CSV import (with template); suggestion via classifier.

  • Config editor:

    • Publish new live config with updated category defaults and vendor map.
    • Localization & Policy editor — edit ui.strings (title, description, button labels, policy link).
    • Policy Block generator — copyable HTML snippet for your cookie policy page.

Cookies analytics tab

  • A dedicated Cookies tab shows aggregated cookie evidence per site with filters:
    • Range (7d/30d/custom), First‑party vs Third‑party, Category, Min confidence, Search (name/domain).
    • “Review queue” lists unknown/low‑confidence cookies (no values), with 1‑click actions:
      • Accept as Definition (global pattern) → POST /api/admin/cookies/definitions:batch
      • Override for this site → POST /api/admin/cookies/overrides
    • Flags (Secure/HttpOnly/SameSite) and retention are displayed; counts and last seen are provided for triage.

Notes

  • Cookie values are never stored; only names and attributes. Categories and vendors are inferred by definitions/overrides and safe heuristics.

Vendors analytics tab

  • Time-series rollup of cookie evidence grouped by vendor.
  • Controls: bucket (hour or day) and date range; multi-select vendors; line or stacked-area modes.
  • Shows a “Top vendors” table for the selected range. Click vendors to focus the chart.
  • Backed by GET /api/admin/analytics/sites/by-key/:siteKey/vendors/rollup?bucket=hour|day&range=….

Artifacts tab

  • Lists recent export artifacts with filename, size, created time, and a copy/download action.
  • Backed by GET /api/admin/exports?siteKey=&limit=&offset= and GET /api/admin/exports/:artifactId.
  • Includes pagination and bulk delete. Select rows and click “Delete selected” to remove.
  • Delete uses DELETE /api/admin/exports/:artifactId (tenant‑checked).

Audit tab

  • Shows recent admin actions (AuditLog) with filters:
    • Quick action chips (e.g., overrides upsert/delete, scans baseline promote, exports start, sites domains add/remove/copy).
    • Date range and action selector.
  • Click a row to open a details drawer (full JSON).
  • Export CSV with current filters via GET /api/admin/audit/logs/export.

Exports page (streamed + jobs)

  • Two modes:
    • Streamed export: downloads CSV/JSON/JSONL directly with progress for large responses.
    • Job export: starts an async job, polls for completion, then exposes an artifact for download and history.
  • Endpoints used:
    • Start: POST /api/admin/exports/consents:start{ jobId }
    • Poll: GET /api/admin/exports/jobs/:id{ status, artifactId? }
    • Download/list: GET /api/admin/exports/:artifactId and GET /api/admin/exports?siteKey=&limit=&offset=
  • CSV export for Cookies tab uses GET /api/admin/analytics/sites/by-key/:siteKey/cookies/export?… and respects the active filters.

How it talks to the Registry

  • Loads live config via GET /v1/config?site_key=...&v=live.
  • Publishes via POST /admin/sites/by-key/:siteKey/configs with publish: true.
  • Lists Sites via GET /admin/sites?limit=50&offset=0 (Site Select remote search uses q, page, pageSize).
  • Consent analytics via GET /admin/analytics/sites/by-key/:siteKey/consents.
  • Consent coverage (Diagnostics) via GET /api/admin/analytics/consent?siteKey=&from=&to=.
  • Experiments demo events via POST /api/admin/analytics/experiments/event and results via GET /api/admin/analytics/experiments.
  • Export via GET /admin/analytics/sites/by-key/:siteKey/consents/export?format=csv|json&range=....
  • Config list and upsert rely on /admin/config endpoints (GET/POST/PUT/DELETE) — adjust paths if your Registry uses different routes.

All admin routes require a Bearer token from your IDP. The token must include cmp.admin via one of: scope, roles (CMP_ADMIN or cmp.admin), or permissions.

UI Routes Overview

Public routes (no auth required):

  • / → redirects to /home
  • /home — landing page with quick links and scanner info
  • /scanner — public scanner view
  • /health — environment and health widgets
  • /login, /signup — authentication and registration pages (AntD)

Protected routes (require Bearer token; guard by cmp.admin where applicable):

  • /sites — Sites list and actions; header maintains selected site
  • /analytics — raw list view (URL‑backed search/pagination)
  • /scans — Saved scans list with a Failed queue and status/requeue actions
  • /overrides — Overrides manager (RBAC: cmp.admin)
  • /export — Exports page (streamed + jobs)
  • /scans — Saved scans list
  • /config — Config list with upsert drawer (create/edit/delete)
  • /config/edit — Advanced editor when needed

Notes

  • Routes above live under a nested AppShell that provides header, sidebar, theme, language and Site Select.
  • Navigation shows a slim progress bar.
  • 403, 404, and 500 UX are provided (Forbidden, NotFound, ErrorBoundary).

Auth Redirects

  • Protected routes use a guard that preserves deep links via redirectTo:
    • Visiting a protected route while logged out redirects to /login?redirectTo=<original>.
    • After login, the user is sent back to the original route.
  • Public pages (/login, /signup, /home, /scanner, /health) are wrapped in a public‑only guard:
    • If already authenticated, the user is redirected to redirectTo or the last protected route, then /sites.
  • Last route persistence:
    • The shell stores the last protected route under localStorage['cmp.portal.lastRoute'] and uses it when redirectTo is absent.

Health

  • The Health page probes only public endpoints and does not require a token:
    • Registry: GET /health/liveness, GET /health/ready, GET /health/config
    • IDP (optional): GET ${VITE_IDP_BASE_URL}/health
  • Each check shows a simple OK/DOWN pill with a Retry button.