feat: expose dashboard JSON API with Bearer-token auth for a separated frontend - #16
Closed
jack482653 wants to merge 11 commits into
Closed
feat: expose dashboard JSON API with Bearer-token auth for a separated frontend#16jack482653 wants to merge 11 commits into
jack482653 wants to merge 11 commits into
Conversation
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tive Bearer handling Final whole-branch review fixes (Important sciwork#2, Minor sciwork#4, Minor sciwork#5): - router.py: add _require_frontend_redirect_url() guard used by both login_spa and oauth_callback_spa. Without it, an unset FRONTEND_REDIRECT_URL caused oauth_callback_spa's failure paths to redirect to a bare relative URL, creating a browser redirect loop. Now both routes fail closed with 503 frontend_redirect_url_not_configured. - auth.py: catch itsdangerous.BadData instead of (BadSignature, SignatureExpired) in verify_api_token — BadData also covers BadPayload, which isn't a subclass of BadSignature, so this is strictly more robust and simpler. - auth.py: make the Authorization header's Bearer scheme match case-insensitively per RFC 7235, instead of requiring exact-case "Bearer ". Adds tests/test_auth.py coverage for: 503 on unset frontend_redirect_url (both login_spa and oauth_callback_spa), the previously-untested oauth_exchange_failed redirect branch, and lowercase "bearer" scheme acceptance. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…curity notes Final whole-branch review fixes (Important sciwork#1, Important sciwork#3, Minor sciwork#6, Minor sciwork#7, Minor sciwork#9) — operational and documentation gaps only, no code changes: - README.md, .env.example, SPEC.md (x2): add /dashboard/oauth/callback/spa alongside the existing /dashboard/oauth/callback in every place operators are told which redirect URI(s) to register in Google Cloud Console. - SPEC.md API Reference table: update the Auth column for the 7 pre-existing /dashboard/api/* rows from "session (401)" to "session or Bearer (401)", matching what require_login actually accepts. Update the Routes section's auth-exemption sentence to also list login/spa and oauth/callback/spa. - SPEC.md SPA Authentication section: note that the frontend should strip the token from the URL via history.replaceState after reading it; that removing an email from ALLOWED_EMAILS revokes its outstanding tokens immediately because require_login re-checks the allowlist on every request; and that the token is signed but not encrypted, so the email is trivially readable from it. - .env.example: note near FRONTEND_REDIRECT_URL that it must be a bare URL with no existing "?" or "#" (the backend appends the fragment/query via string concatenation). Note near FRONTEND_ORIGINS to avoid a wildcard/overly-broad value since it would let any origin read /health and probe the API cross-origin. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The separated frontend project is not finalized yet (currently a standalone repo). Refer to it generically as a future pure-client-side frontend instead of naming it, to avoid documenting an unsettled detail. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #16 +/- ##
==========================================
+ Coverage 75.41% 76.90% +1.48%
==========================================
Files 16 16
Lines 720 775 +55
==========================================
+ Hits 543 596 +53
- Misses 177 179 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
jack482653
marked this pull request as draft
August 15, 2026 08:31
Collaborator
Author
|
Superseded by a monorepo-integration design (frontend served same-origin, session-cookie auth only — no Bearer/CORS/SPA-OAuth needed). Closing without merging; see docs/superpowers/specs/2026-08-15-frontend-monorepo-integration-design.md on the follow-up branch. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Exposes the dashboard's functionality as a token-authenticated JSON API so a separated, statically-exported (pure client-side) frontend can consume it cross-origin, without breaking the existing server-rendered dashboard.
Most of the JSON API already existed under
/dashboard/api/*. The gap was auth: the existing flow only supports a same-origin session cookie, which doesn't work reliably for a static SPA hosted on a different origin (no server to proxy through, and cross-site cookies are increasingly restricted by browsers). This adds a parallel, stateless Bearer-token auth path alongside the existing session-cookie flow.What changed
FRONTEND_ORIGINS,FRONTEND_REDIRECT_URL,AUTH_TOKEN_TTL_SECONDSenv vars.auth.issue_api_token/auth.verify_api_token— signed, time-limited tokens viaitsdangerous.URLSafeTimedSerializer(already a dependency, no new library), signed with the existingSESSION_SECRET.require_loginnow accepts either the legacy session cookie or anAuthorization: Bearer <token>header — every existing/dashboard/api/*route gains Bearer support with zero route-handler changes.GET /dashboard/api/me— new endpoint for a frontend to bootstrap/validate its session.GET /dashboard/login/spa/GET /dashboard/oauth/callback/spa— a parallel Google OAuth flow that, on success, redirects the browser to{FRONTEND_REDIRECT_URL}#token=<token>(token in the URL fragment, never sent to any server); on failure, redirects with?error=.... Fails closed with a 503 ifFRONTEND_REDIRECT_URLisn't configured, rather than looping.FRONTEND_ORIGINS, narrow method/header allowlist, noallow_credentials(auth is Bearer-only, not cookie-based, so no credentialed-CORS/CSRF exposure)./dashboard,/dashboard/events/{slug},/dashboard/webhook-logs) is untouched and keeps working unchanged — left in place deliberately, to be removed in a follow-up once a separated frontend reaches parity.README.md,SPEC.md,.env.example) updated accordingly. The separated frontend project isn't finalized yet, so it's referred to generically rather than by name.Testing
ruff checkandruff format --checkclean.Process note
Implemented via a 7-task plan, each task independently reviewed, followed by a full-branch security-focused review (no Critical findings; a few Important/Minor doc and robustness gaps found and fixed in one follow-up pass, re-reviewed clean).
One known, low-risk follow-up:
README.md's "Production / Deployment" section still lists only the legacy OAuth redirect URI, not the new SPA one — a one-line doc gap, self-diagnosing (Google's own error names the missing URI if hit).🤖 Generated with Claude Code