There is a newer version of the record available.

Published May 23, 2026 | Version v1.0.0

chain simple forms / surveys into longer runs using the power of R to generate pretty feedback and complex designs https://formr.org

  • 1. Wilhelm Wundt Institute of Psychology, University of Leipzig
  • 2. Georg Elias Müller Institute of Psychology, University of Goettingen

Description

Heads up: every existing API integration breaks on upgrade. All current access tokens are invalidated and every API client secret is wiped. Plan a maintenance window, then have each credential owner mint a new secret, select their scopes, and (optionally) restrict the credential to specific runs. See UPGRADING-v1.0.0.md for the audit + rotation checklist before you bump FORMR_TAG.

Added

  • Versioned v1 API at /api/v1/<resource>. OAuth2 with one-hour access tokens. Resources include user, surveys, runs/{name}, and per-run sub-resources sessions, results, files, structure. Twelve scopes — user:read/write, survey:read/write, run:read/write, session:read/write, data:read, file:read/write — checked before the resource is looked up, so a token without the right scope can't probe which runs or surveys exist.
  • New admin level "API access" (admin = 2). Only users at this level (or above) can mint or use API credentials. Existing admin = 1 accounts keep their web-admin rights but lose API access until a SuperAdmin promotes them from the user-management page.
  • Multiple labelled API credentials per user. The API tab at /admin/account is now a credential table — each row has its own label, scopes, run allowlist, rotate button, and delete button. A common pattern: one narrow read-only credential for a dashboard, plus a broader credential for a cron job. Labels are unique per user; the label internal is reserved.
  • One-time secret display. Client secrets are shown only at issuance and rotation; the server stores a hash. A forgotten secret must be rotated, not recovered.
  • Per-credential run allowlist. Restrict a credential to one or more specific runs at creation / rotation; an empty list means unrestricted (the legacy behaviour). Internal R-callback tokens (used by OpenCPU rendering) are automatically pinned to the run that minted them.
  • Unit-session history endpoint GET /v1/runs/{name}/unit_sessions. One row per (participant × unit × iteration) — the complement to /v1/runs/{name}/sessions, which only exposes each participant's current unit. Rows arrive ordered so consecutive rows per participant are trajectory edges. Filters: ?session=, ?testing=, ?since=; pagination via limit (default 1000, max 10000) and offset. Scope: session:read. The R package wraps this as formr_api_unit_sessions().
  • Trajectory-Sankey by default in the Overview script. New runs get an Overview script that renders a Sankey of participants' actual paths through the study, with terminal arrows to Completed, Expired, and Active @ <position>. Re-iterations (diary, longitudinal) collapse to one node per position, with an (avg N visits) label suffix when the average exceeds one. Customise freely — drop in any R, knitr chunks, plotly / ggplot, raw markdown. Existing Overview scripts are not touched.

Changed (BREAKING)

  • OAuth tokens and client secrets are now stored as hashes at rest. On upgrade, all access tokens, refresh tokens, and authorization codes are truncated, and every client secret is zeroed. Every API client must mint a new secret from /admin/account#api after upgrade. Plan a maintenance window for any unattended cron jobs that hold long-lived tokens.
  • Credentials must now explicitly request scopes. The old "all scopes by default" no longer holds — after upgrade, users open the API tab, pick the read/write scopes their credential should carry, optionally restrict it to specific runs, then click Create credential / Rotate. Tokens minted under the old default no longer work.
  • OAuthHelper reshaped for multi-client per user. If you maintain a downstream integration that calls OAuthHelper directly, the single-client lookups (getClient(User), refreshToken(User, ...), deleteClient(User)) are replaced by listClientsForUser, getClientForUser, rotateClient, deleteClient(User, $clientId), plus an emergency-revoke deleteAllClientsForUser. In-repo callers were migrated in the same commit.

Fixes

  • Overview script errors are now visible to the admin. Errors in the Overview script no longer disappear into a blank box — they appear as a red banner with a collapsible debugger output, in the same spot the rendered iframe would have shown.
  • R / OpenCPU errors surface in admin and cron contexts. Real participants still see only the public message; admins (and the queue daemons) now see the actual error body, matching the behaviour the test runner already had.
  • The Overview script's settings chunk no longer leaks the embedded API token. Previously the chunk source was echoed in admin / test contexts, briefly showing the .formr$access_token = '…' line that authenticates the embedded R session. Side effects (token + helper assignment) still apply to subsequent chunks.
  • Survey import via Google Sheet no longer leaks a tmp file when the upload-items step throws.
  • Credential creation now commits or rolls back as a single transaction. Previously a partial failure could leave a stub credential row with an empty scope / secret.

Security

  • Strict identifier validation in ORDER BY clauses. The previous pass-through let any string flow into ORDER BY verbatim — a future caller forwarding a ?sort= request param would have been a SQL-injection sink. Only column or table.column (with optional backticks) are accepted now; SQL function calls require an explicit DB::raw() escape hatch.
  • Parameterised the last interpolating SQL helpers. Two LIKE filters in user management (superadmin email search) and admin results (session filter) were concatenating user input into the SQL literal; both are now bound through placeholders with wildcard escaping. Same pattern fixed in DB::like().
  • Operators are encouraged to upgrade and to rotate any OAuth client secrets after upgrading. A detailed advisory will follow once adoption is broader.

Schema

  • Patch 050_hash_oauth_tokens.sql — switch OAuth tokens to hashed storage; truncates token tables.
  • Patch 051_hash_client_secrets.sql — zero existing client secrets so the new hashed-storage path is used on first rotation.
  • Patch 052_oauth_client_runs.sql — add the per-credential run allowlist, widen the scope column, clear "default scope" rows.
  • Patch 053_oauth_access_token_run_ids.sql — per-token run allowlist for internal R-callback tokens.
  • Patch 054_oauth_client_labels.sqloauth_clients.label with UNIQUE(user_id, label), enabling multiple labelled credentials per user.

Files

rubenarslan/formr.org-v1.0.0.zip

Files (4.3 MB)

Name Size Download all
md5:823fd9d78c41e114f50189a3a2032abe
4.3 MB Preview Download

Additional details

Related works