Skip to content

v1.8.4 — #573: API session-from-token cookie-bridge for UI test automation

Choose a tag to compare

@clem-field clem-field released this 27 May 22:49

v1.8.4 — #573: POST /api/v1/sessions/from_token cookie-bridge for UI test automation

Closes #573 — the Layer 2 prerequisite of the UI testing umbrella
(#572). Adds a single new API endpoint that exchanges a SPARC API
Bearer token for a Rails session cookie, so test runners (Playwright,
Cypress, headless Chromium) can drive the SPARC UI as an
authenticated user without screen-scraping the login form.

Endpoint

POST /api/v1/sessions/from_token
Authorization: Bearer sparc_sa_ (or OIDC JWT, per
SPARC_API_AUTH mode)

204 No Content + Set-Cookie: on valid token
401 Unauthorized, no Set-Cookie on invalid / missing
/ revoked token

Attach the resulting cookie to subsequent web requests to drive
the UI as the bridged user.

Files

app/controllers/api/v1/sessions_controller.rb (new)
Inherits from ApplicationController (for session + cookies
middleware) but includes ApiAuthentication for Bearer-token
verification. Skips form-CSRF / require_authentication /
session-timeout / password-reset. Manually emits
api_session_bridge_failed on missing/invalid token so failed
attempts are visible in the audit log alongside login_failure.

config/routes.rb
POST /api/v1/sessions/from_token routes to sessions#from_token.

app/controllers/api/v1/discovery_controller.rb
Endpoint registered in ENDPOINT_REGISTRY — /api/v1/available
now advertises it.

config/initializers/rack_attack.rb
Dedicated throttle bucket api/sessions_from_token/min/ip keyed
on client IP (not token) so a brute-force from one IP can't
sidestep by rotating tokens. Reuses the
rate_limit_login_failures_per_minute config knob.

app/models/audit_event.rb
+ api_session_bridged, api_session_bridge_failed in ACTIONS
+ both categorized under "Authentication" alongside
login_success / login_failure / logout / password_change

app/models/sparc_config.rb
VERSION 1.8.3 → 1.8.4.

docs/api/introduction.md
New "UI Automation: Bearer-Token to Session Cookie Bridge"
section with a Playwright (Python) example: token → cookie →
page.goto.

Specs

spec/requests/api/v1/sessions_spec.rb (9 examples)
- 204 + Set-Cookie for valid admin token
- SA-token bridge (the primary Playwright use case)
- api_session_bridged audit on success (records auth_mode +
is_service_account flag)
- is_service_account: true recorded for SA bridges
- 401 + api_session_bridge_failed reason: missing_token when
no Authorization header
- 401 + api_session_bridge_failed reason: invalid_token for
bad Bearer
- 401 + no Set-Cookie when token has been revoked / destroyed
- Bridged cookie authenticates subsequent web requests (no
/login redirect)
- Discovery output lists /api/v1/sessions/from_token

Security guardrails (per #573 issue body)

  • No password downgrade — Bearer required; revoked tokens 401
  • No CSRF bypass — token via Authorization header, not cookie
  • Audit trail — both success + failure paths emit events
  • Session expiry — inherits SPARC_SESSION_TIMEOUT_MINUTES
  • Rate limit — dedicated IP-keyed bucket, same period as login
    failures
  • SA tokens bridge by design (primary Playwright use case);
    bridged session has exactly the SA's privileges (admin? iff
    the SA is admin per #536)

Test result

bundle exec rspec 2579 examples, 0 failures, 2 pending (+9 new)
bin/rubocop on changed files 0 offenses

Upgrade notes

  • No schema changes. No data migration.
  • No env var or config changes required.
  • Existing /api/v1/* clients unaffected.
  • The new endpoint is opt-in: it only fires when a caller
    explicitly POSTs to /api/v1/sessions/from_token.

What's next (v1.8.5)

Queued: a small security hardening bundle covering #341 (XML
upload root-element/namespace fingerprinting), #531 (opt-in
GuardDuty S3 tag check on blob serving), and the CSP report-uri
slice of #528. Will land as a single PR matching the
bundle-lightweight-fixes pattern.

🤖 Generated with Claude Code