Fix CSP-blocked social login and add connected-accounts management - #683
Conversation
📝 WalkthroughWalkthroughAdds JSON-based social login start handling, signed-in account linking/disconnect flows, a new account connections API and UI, rate-limit branching for JSON clients, and matching tests/docs updates. ChangesSocial Login JSON Flow and Account Connections
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant UI
participant AuthProviderHandler
participant OAuthProvider
participant AccountConnectionsAPI
UI->>AuthProviderHandler: POST /auth/:provider (Accept: application/json)
AuthProviderHandler-->>UI: { ok: true, authorizeUrl } + cookie
UI->>OAuthProvider: navigate to authorizeUrl
OAuthProvider-->>AuthProviderHandler: callback with code
AuthProviderHandler->>AuthProviderHandler: check session and existing connection
AuthProviderHandler-->>UI: redirect /account?oauthLinked or oauthError
UI->>AccountConnectionsAPI: GET /account/connections.json
AccountConnectionsAPI-->>UI: connections + availableProviders
UI->>AccountConnectionsAPI: POST disconnect
AccountConnectionsAPI-->>UI: updated connections or 400 error
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
🔎 Preview deployed: https://kody-pr-683.kody-a99.workers.dev Worker: Mocks:
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e9892ae. Configure here.

Fixes the two issues found while testing social login live on heykody.dev, in one PR:
1. Every provider button errored (CSP)
The login buttons were native form POSTs. The client router intercepts document form submits and replays them as
fetch, whose followed redirect to the provider origin violatesconnect-src 'self'— and even unintercepted forms would hitform-action 'self'on the post-submit redirect. The mock providers masked this in tests because their authorize URL is same-origin.Fix:
POST /auth/:providernow returns{ authorizeUrl }JSON when the request sendsAccept: application/json, and the UI (login page + account card) fetches it and performs a top-level navigation to the provider, which CSP does not restrict. The 302 behavior remains for non-JSON clients, and the rate-limiter's 303-to-login fallback now only applies to non-JSON requests. No CSP loosening needed.Demo with a real (non-mock) GitHub client id in dev: clicking "Continue with GitHub" now cleanly navigates to github.com with zero console errors.
2. Connect providers while signed in
/accountgains a Connected accounts card (backed byGET/POST /account/connections.json):DELETEitself so concurrent disconnects cannot race past a separate pre-check; the UI disables the button with an explanatory tooltip.connection-conflicterror, never an account switch), success redirects to/account?oauthLinked=<provider>with a confirmation message, and signed-in errors land on/account?oauthError=<code>instead of bouncing through/login.Tests
auth-provider.node.test.ts: 3 new tests — JSON start mode (authorize URL + state cookie, JSON errors), signed-in link / re-link no-op / cross-user conflict / list / disconnect, and the disconnect guard for passwordless social-only accounts.e2e/social-login.spec.ts: extended to cover the connections card end-to-end (disabled disconnect on the only sign-in method, connect Google, disconnect Google) via the mock providers.System recap — extends existing primitives (medium risk)
Mode: recap · Base:
main@1011ace3· Head:418ebf2fClassification: extends — reshapes the social-login start contract (JSON mode) inside
app-sessionsand adds connection management; no new primitive (app-sessionscode list updated inprimitives.yaml).Primitives touched
app-sessions/account/connections.jsonlist/disconnect with an atomic last-sign-in-method guardapp-ui/accountSystem map
Change flow
sequenceDiagram participant B as Browser (login or account card) participant K as Worker participant P as Provider B->>K: fetch POST /auth/:provider (Accept: json) K-->>B: { authorizeUrl } + kody_oauth_login cookie B->>P: top-level navigation (CSP-safe) P-->>B: 302 /auth/:provider/callback?code&state B->>K: GET callback alt signed in K-->>B: link -> /account?oauthLinked=… (conflict/em errors -> /account?oauthError=…) else signed out K-->>B: sign in / auto-link / signup as before endInvariants
per-user-isolation: connections list/disconnect operate strictly on the authenticated user's rows (WHERE user_id = ?); linking never reassigns an identity that belongs to another user.Summary by CodeRabbit