Skip to content

Fix CSV exports returning no data for OIDC-authenticated sessions#11845

Merged
nbudin merged 1 commit into
mainfrom
investigate-signup-spy-export-bug
Jul 22, 2026
Merged

Fix CSV exports returning no data for OIDC-authenticated sessions#11845
nbudin merged 1 commit into
mainfrom
investigate-signup-spy-export-bug

Conversation

@nbudin

@nbudin nbudin commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #11843 (signup spy export returns headers with no data) — and, as it turns out, every other CSV export in the app for the same reason.

Root cause: CSV export buttons (ExportButton.tsx) rendered a plain <a href="/csv_exports/..."> link. But this app's current auth model authenticates via a bearer token attached to fetch/GraphQL requests (AuthenticationManager), not a Devise session cookie — OIDC sign-in (the default "Sign In" flow) never establishes one, only an in-memory access token plus an HttpOnly refresh-token cookie scoped to /oauth_session/* that CsvExportsController never reads.

A plain link click therefore hits the export routes with no usable credentials. CsvExportsController#signup_changes (and its siblings) don't crash on this — the Pundit scopes correctly-but-silently resolve to nothing for an unauthenticated request, so the response is a valid 200 CSV with headers and zero data rows. This affects every route under /csv_exports/* (coupons, event proposals, orders, run signups, run signup changes, runs, user con profiles, users, signup changes), since they all share the same export button component.

Fix: ReactTableExportButton now fetches the export URL itself, attaching the same bearer token Apollo Client attaches to GraphQL calls (via AuthenticationManagerContext), then triggers a client-side blob download using the filename from the Content-Disposition header — instead of relying on a plain navigation.

Test plan

  • Added test/controllers/csv_exports_controller_test.rb coverage:
    • happy path export with a signed-in admin (Devise session) returns real data
    • signed-out request returns an empty (but non-crashing) export
    • a request with a properly-scoped bearer token attached (matching the real OIDC scope list) returns real data, while the same request without the header comes back empty — reproducing the reported bug and confirming the fix's mechanism
  • bin/rails test test/controllers/csv_exports_controller_test.rb — 6/6 passing
  • eslint on the changed frontend file — clean
  • yarn run ts:check — blocked locally by unrelated system memory pressure; please run before merging
  • Manual smoke test: click an export button (e.g. Signup Spy) in a real OIDC-authenticated browser session and confirm the downloaded CSV has data

🤖 Generated with Claude Code

CSV export links were plain <a href> navigations, but this app now
authenticates via a bearer token attached to fetch/GraphQL requests
rather than a Devise session cookie (OIDC sign-in never establishes
one). A plain link click therefore hit the export routes
unauthenticated, and the policy scopes correctly-but-silently
returned zero rows -- a CSV with headers but no data.

ExportButton now fetches the CSV with the same bearer token Apollo
attaches, then triggers a client-side blob download. This fixes every
export in the app (signup spy, orders, coupons, attendees, etc.),
since they all share this component.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Code Coverage Report: Only Changed Files listed

Package Base Coverage New Coverage Difference
app/controllers/concerns/send_csv.rb 🔴 25% 🟢 100% 🟢 75%
app/controllers/csv_exports_controller.rb 🔴 45.76% 🔴 47.46% 🟢 1.7%
app/models/signup.rb 🟢 87.14% 🟢 90% 🟢 2.86%
app/models/user_con_profile.rb 🟠 67.26% 🟠 68.14% 🟢 0.88%
app/policies/signup_change_policy.rb 🔴 0% 🟠 66.67% 🟢 66.67%
app/presenters/tables/signup_changes_table_results_presenter.rb 🔴 0% 🟢 84.62% 🟢 84.62%
app/presenters/tables/signups_table_results_presenter.rb 🔴 0% 🟠 60% 🟢 60%
app/presenters/tables/table_results_presenter.rb 🔴 0% 🟢 75.22% 🟢 75.22%
Overall Coverage 🟢 54.41% 🟢 54.6% 🟢 0.19%

Minimum allowed coverage is 0%, this run produced 54.6%

@nbudin
nbudin merged commit cf1cce4 into main Jul 22, 2026
38 of 39 checks passed
@nbudin
nbudin deleted the investigate-signup-spy-export-bug branch July 22, 2026 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Signup spy export is broken

1 participant