Merge branch 'fix/v1474-auth' into release/v1.47.4

# Conflicts:
#	src/__tests__/baselines/version-stamp-comments.json
This commit is contained in:
Marc-André Bombeck committed 2026-11-11 21:32:28 +01:00
commit 212ff9ee55
33 files changed
+1177 -151

No files matched your search

@@ -1,7 +1,7 @@
{
"$comment": "Production files over 800 lines and their length. Maintained by src/__tests__/source-size-ratchet.test.ts.",
"count": 84,
"total": 94047,
"total": 94006,
"entries": {
"app/medications/page-client.tsx": 839,
"components/charts/mood-chart.tsx": 1166,
@@ -12,7 +12,7 @@
"components/insights/derived/vitals-dashboard.tsx": 1033,
"components/insights/health-score-card.tsx": 1023,
"components/insights/insights-tab-strip.tsx": 1087,
"components/measurements/measurement-form.tsx": 815,
"components/measurements/measurement-form.tsx": 812,
"components/medications/detail/medication-detail-tabs.tsx": 1157,
"components/medications/medication-table.tsx": 853,
"components/medications/wizard/wizard-payload.ts": 1414,
@@ -82,7 +82,7 @@
"lib/search/search-records.ts": 996,
"lib/sharing/grants.ts": 806,
"lib/support/report.ts": 903,
"lib/telegram-webhook-handlers.ts": 1341,
"lib/telegram-webhook-handlers.ts": 1335,
"lib/validations/inbound-documents.ts": 1018,
"lib/withings/sync-sleep.ts": 884,
"lib/withings/sync.ts": 933,
@@ -1,7 +1,7 @@
{
"$comment": "Production functions over 200 lines, keyed \"file :: name\", and their length. Maintained by src/__tests__/source-size-ratchet.test.ts.",
"count": 344,
"total": 111495,
"count": 343,
"total": 111120,
"entries": {
"app/achievements/page.tsx :: AchievementsPage": 206,
"app/api/admin/backups/upload/route.ts :: apiHandler(arg)": 275,
@@ -17,7 +17,7 @@
"app/api/devices/route.ts :: apiHandler(arg)": 226,
"app/api/encounters/[id]/route.ts :: apiHandler(arg) #2": 269,
"app/api/environment/route.ts :: apiHandler(arg)": 201,
"app/api/export/health-record/route.ts :: apiHandler(arg)": 241,
"app/api/export/health-record/route.ts :: apiHandler(arg)": 240,
"app/api/ingest/medication/route.ts :: apiHandler(arg)": 259,
"app/api/insights/cards/route.ts :: apiHandler(arg)": 211,
"app/api/integrations/status/route.ts :: apiHandler(arg)": 476,
@@ -38,8 +38,8 @@
"app/api/record-settings/[family]/route.ts :: apiHandler(arg) #2": 262,
"app/api/user/ai-provider/route.ts :: apiHandler(arg) #2": 248,
"app/api/withings/callback/route.ts :: apiHandler(arg)": 247,
"app/auth/login/page.tsx :: LoginPage": 569,
"app/auth/register/page.tsx :: RegisterPage": 285,
"app/auth/login/page.tsx :: LoginPage": 561,
"app/auth/register/page.tsx :: RegisterPage": 283,
"app/coach/conversations/page.tsx :: CoachConversationsBody": 249,
"app/coach/plans/page.tsx :: CoachPlansBody": 216,
"app/insights/catalog/page.tsx :: MetricCatalogPage": 204,
@@ -64,7 +64,7 @@
"components/admin/user-management-section.tsx :: UserManagementSection": 622,
"components/admin/web-push-vapid-section.tsx :: WebPushVapidSection": 217,
"components/auth/claim-profile.tsx :: ClaimProfile": 429,
"components/auth/mfa-login-step.tsx :: MfaLoginStep": 217,
"components/auth/mfa-login-step.tsx :: MfaLoginStep": 215,
"components/charts/compliance-heatmap.tsx :: ComplianceHeatmap": 325,
"components/charts/health-chart.tsx :: HealthChart": 470,
"components/charts/medication-compliance-chart.tsx :: MedicationComplianceChart": 543,
@@ -73,7 +73,7 @@
"components/charts/trend-card.tsx :: TrendCard": 365,
"components/clinician/clinician-view.tsx :: ClinicianView": 223,
"components/command-palette/command-palette.tsx :: PaletteBody": 333,
"components/custom-metrics/custom-metric-chart.tsx :: CustomMetricChart": 231,
"components/custom-metrics/custom-metric-chart.tsx :: CustomMetricChart": 228,
"components/custom-metrics/custom-metric-detail.tsx :: CustomMetricDetail": 277,
"components/custom-metrics/custom-metric-form.tsx :: CustomMetricForm": 214,
"components/custom-metrics/custom-metric-history-list.tsx :: CustomMetricHistoryList": 302,
@@ -138,7 +138,7 @@
"components/labs/lab-history-list.tsx :: LabHistoryList": 534,
"components/labs/ocr-review-dialog.tsx :: OcrReviewDialog": 359,
"components/labs/ocr-row-editor.tsx :: OcrRowEditor": 378,
"components/layout/auth-shell.tsx :: AuthShell": 518,
"components/layout/auth-shell.tsx :: AuthShell": 483,
"components/layout/bottom-nav.tsx :: BottomNav": 262,
"components/layout/capture-picker.tsx :: CapturePicker": 235,
"components/layout/sidebar-nav.tsx :: SidebarNav": 356,
@@ -146,7 +146,7 @@
"components/links/entity-link-picker.tsx :: EntityLinkPicker": 392,
"components/measurement-reminders/vorsorge-dashboard-card.tsx :: VorsorgeDashboardCard": 215,
"components/measurement-reminders/vorsorge-section.tsx :: VorsorgeSection": 352,
"components/measurements/measurement-form.tsx :: MeasurementForm": 500,
"components/measurements/measurement-form.tsx :: MeasurementForm": 493,
"components/medications/detail/efficacy/efficacy-chart.tsx :: EfficacyChart": 361,
"components/medications/detail/medication-detail-tabs.tsx :: MedicationDetailTabs": 767,
"components/medications/dose-history-add-dialog.tsx :: LedgerAddDialog": 223,
@@ -161,7 +161,7 @@
"components/medications/medication-table.tsx :: MedicationTableRowItem": 377,
"components/medications/scheduling/natural-language-extractor.tsx :: NaturalLanguageExtractor": 214,
"components/medications/scheduling/schedule-history-timeline.tsx :: EraDialog": 219,
"components/medications/scheduling/schedule-history-timeline.tsx :: ScheduleHistoryTimeline": 256,
"components/medications/scheduling/schedule-history-timeline.tsx :: ScheduleHistoryTimeline": 254,
"components/medications/sections/api-tokens-row.tsx :: ApiTokensRow": 296,
"components/medications/sections/composition-section.tsx :: CompositionEditor": 362,
"components/medications/sections/inventory-dialogs.tsx :: AddInventoryDialog": 238,
@@ -232,7 +232,7 @@
"components/timeline/life-event-form.tsx :: LifeEventForm": 281,
"components/timeline/timeline-chart.tsx :: ChartBody": 236,
"components/timeline/timeline-chronicle.tsx :: TimelineChronicle": 246,
"components/timeline/timeline-view.tsx :: TimelineView": 486,
"components/timeline/timeline-view.tsx :: TimelineView": 478,
"components/ui/responsive-sheet.tsx :: ResponsiveSheet": 239,
"components/vaccinations/catalog-picker.tsx :: CatalogPicker": 221,
"components/vaccinations/custom-vaccine-sheet.tsx :: CustomVaccineSheet": 246,
@@ -284,8 +284,8 @@
"lib/google-health/data-points.ts :: forEachDataPointPage > walk": 223,
"lib/google-health/sync-core.ts :: upsertGoogleHealthMeasurements": 452,
"lib/illness/correlation.ts :: computeIllnessCorrelation": 233,
"lib/import/labs-csv.ts :: importLabsCsv": 307,
"lib/import/labs-csv.ts :: importLabsCsv > flush": 229,
"lib/import/labs-csv.ts :: importLabsCsv": 305,
"lib/import/labs-csv.ts :: importLabsCsv > flush": 227,
"lib/insights/comprehensive-aggregator.ts :: buildFromRollups": 217,
"lib/insights/comprehensive-generate.ts :: generateComprehensiveInsight": 876,
"lib/insights/features.ts :: extractFeatures": 796,
@@ -329,7 +329,7 @@
"lib/medications/list-read.ts :: buildMedicationsList": 308,
"lib/medications/scheduling/dose-history.ts :: reconstructDoseHistory": 218,
"lib/multipart/stream-to-disk.ts :: streamMultipartToDisk": 272,
"lib/notifications/dispatcher.ts :: dispatchNotification": 378,
"lib/notifications/dispatcher.ts :: dispatchNotification": 368,
"lib/notifications/senders/apns.ts :: sendViaApns": 299,
"lib/notifications/senders/web-push.ts :: sendViaWebPush": 255,
"lib/personal-records/pr-detection-worker.ts :: detectPersonalRecordsForUser": 239,
@@ -339,12 +339,12 @@
"lib/support/report.ts :: collectSupportReportFacts": 332,
"lib/targets/build-response.ts :: buildTargetsResponse": 330,
"lib/targets/vitals-builder.ts :: buildVitalTargets": 324,
"lib/telegram-webhook-handlers.ts :: handleCallback": 599,
"lib/telegram-webhook-handlers.ts :: handleCallback": 590,
"lib/telegram-webhook-handlers.ts :: handleTextMessage": 337,
"lib/withings/sync-activity.ts :: syncUserActivity": 377,
"lib/withings/sync-ecg.ts :: syncUserEcg": 202,
"lib/withings/sync-sleep.ts :: syncUserSleep": 414,
"lib/withings/sync.ts :: syncUserMeasurements": 337,
"proxy.ts :: proxy": 471
"proxy.ts :: proxy": 467
}
}
@@ -1,7 +1,7 @@
{
"$comment": "Comment lines that open with a release version, per file under src/. Maintained by src/__tests__/version-stamp-ratchet.test.ts.",
"count": 2885,
"total": 9198,
"total": 9193,
"entries": {
"__tests__/account-payload-consumer-guard.test.ts": 2,
"__tests__/acting-account-boundary-guard.test.ts": 7,
@@ -136,7 +136,7 @@
"app/api/auth/codex/device-start/route.ts": 1,
"app/api/auth/login/__tests__/native-token.test.ts": 2,
"app/api/auth/login/__tests__/route.test.ts": 1,
"app/api/auth/login/route.ts": 5,
"app/api/auth/login/route.ts": 4,
"app/api/auth/me/coach-prefs/route.ts": 7,
"app/api/auth/me/cycle-prefs/route.ts": 1,
"app/api/auth/me/devices/[id]/route.ts": 3,
@@ -556,7 +556,7 @@
"app/api/workouts/route.ts": 2,
"app/auth/claim/page.tsx": 1,
"app/auth/login/page.tsx": 5,
"app/auth/register/page.tsx": 4,
"app/auth/register/page.tsx": 3,
"app/c/[token]/__tests__/page.test.tsx": 2,
"app/c/[token]/d/[id]/__tests__/route.test.ts": 1,
"app/c/[token]/d/[id]/route.ts": 1,
@@ -1094,7 +1094,7 @@
"components/layout/__tests__/shared-record-chrome.test.tsx": 1,
"components/layout/__tests__/sidebar-nav.test.tsx": 3,
"components/layout/account-switcher-menu.tsx": 1,
"components/layout/auth-shell.tsx": 14,
"components/layout/auth-shell.tsx": 13,
"components/layout/bottom-nav.tsx": 8,
"components/layout/capture-picker.tsx": 5,
"components/layout/nav-model.ts": 17,
@@ -2885,7 +2885,7 @@
"lib/withings/sync.ts": 6,
"lib/withings/webhook-handler.ts": 3,
"lib/workouts/list-read.ts": 1,
"proxy.ts": 27,
"proxy.ts": 25,
"types/analytics.ts": 8,
"types/backups.ts": 1
}
@@ -161,6 +161,25 @@ describe("proxy.ts DEMO_MODE mutation allowlist", () => {
expect(proxy(makeRequest("/api/search", "DELETE")).status).toBe(403);
});
it("lets a visitor sign out, report page timings and pick a language", () => {
// Each touches only this visitor: their own session, a log line, their
// own language cookie (the locale route leaves the shared account's row
// alone under DEMO_MODE). Refused, sign-out stayed signed in and every
// page load sent the refused locale write and the timings again.
for (const [path, method] of [
["/api/auth/logout", "POST"],
["/api/internal/web-vitals", "POST"],
["/api/auth/me/locale", "PUT"],
] as const) {
expect(
proxy(makeRequest(path, method)).status,
`${method} ${path} is blocked in the demo`,
).not.toBe(403);
}
// The rest of the account's settings stay closed.
expect(proxy(makeRequest("/api/auth/me/timezone", "PUT")).status).toBe(403);
});
it("still blocks a health-data mutation (POST /api/measurements)", () => {
const res = proxy(makeRequest("/api/measurements", "POST"));
expect(res.status).toBe(403);
@@ -87,7 +87,7 @@ describe("proxy early exits carry the baseline security headers", () => {
it("unauthenticated page redirect to /auth/login", () => {
const res = proxy(makeRequest("/dashboard"));
expect(res.headers.get("location")).toMatch(/\/auth\/login$/);
expect(res.headers.get("location")).toMatch(/\/auth\/login(\?|$)/);
expectBaselineHeaders(res);
});
@@ -82,7 +82,7 @@ describe("proxy.ts onboarding redirect (v1.4.22 C4)", () => {
expect(res.status).toBe(200);
expect(res.headers.get("location")).toBeNull();
const anonymous = proxy(makeRequest("/insights"));
expect(anonymous.headers.get("location")).toMatch(/\/auth\/login$/);
expect(anonymous.headers.get("location")).toMatch(/\/auth\/login(\?|$)/);
});
it("passes through when hl_onboarding cookie is absent (completed users)", () => {
@@ -123,7 +123,7 @@ describe("proxy.ts onboarding redirect (v1.4.22 C4)", () => {
// were still in place it would short-circuit through public-path
// handling and 200 instead.
expect([307, 308]).toContain(res.status);
expect(res.headers.get("location")).toMatch(/\/auth\/login$/);
expect(res.headers.get("location")).toMatch(/\/auth\/login(\?|$)/);
});
it("still treats subroutes /onboarding/<x> as the onboarding surface", () => {
+40
View File
@@ -0,0 +1,40 @@
/**
* The edge sends a visitor without a session to the sign-in page with the
* page they asked for in `next`, so a home-screen shortcut or a notification
* tap still arrives there after signing in. The sign-in page re-checks the
* value (`sanitizeSameOriginPath`); the proxy only ever puts in the
* request's own path and query.
*/
import { describe, expect, it, vi } from "vitest";
import { NextRequest } from "next/server";
vi.mock("@/lib/process-type", () => ({
shouldRunWeb: () => true,
}));
import { proxy } from "../proxy";
function signInRedirect(pathAndQuery: string): URL {
const res = proxy(new NextRequest(`http://localhost${pathAndQuery}`));
expect([307, 308]).toContain(res.status);
return new URL(res.headers.get("location")!, "http://localhost");
}
describe("proxy.ts sign-in redirect", () => {
it.each([
"/measurements?add=WEIGHT&source=shortcut",
"/mood?add=1",
"/medications",
"/?source=pwa",
])("carries %s to the sign-in page", (target) => {
const location = signInRedirect(target);
expect(location.pathname).toBe("/auth/login");
expect(location.searchParams.get("next")).toBe(target);
});
it("sends the bare start page to a bare sign-in page", () => {
const location = signInRedirect("/");
expect(location.pathname).toBe("/auth/login");
expect(location.search).toBe("");
});
});