Skip to content

feat: improve OAuth broker credential handling (labels, multi-account, reconnect, stale cleanup)#162

Open
sean-at-jentic wants to merge 30 commits intomainfrom
fix/connect-label-via-redirect
Open

feat: improve OAuth broker credential handling (labels, multi-account, reconnect, stale cleanup)#162
sean-at-jentic wants to merge 30 commits intomainfrom
fix/connect-label-via-redirect

Conversation

@sean-at-jentic
Copy link
Copy Markdown
Contributor

Summary

A batch of improvements to how Pipedream OAuth broker credentials are managed end-to-end.

Label routing via success_redirect_uri

Previously labels were stored at connect-link creation time, keyed by app_slug, which caused collisions when connecting multiple accounts for the same app (e.g. two Gmail accounts). Labels are now passed through Pipedream's success_redirect_uri callback, so each connection carries its own label and they cannot overwrite each other.

Multi-account support (same app_slug)

Added migration 0002 to include account_id in the oauth_broker_accounts unique constraint and primary key — previously two Gmail accounts would collide on the same row. compose.yml now mounts alembic/ as a volume so new migrations are picked up without an image rebuild.

Reconnect button

New Reconnect button (↺) on both the OAuth Brokers page and the Credentials page. Generates a fresh connect link; on completion, the new account is confirmed in DB before the old one is deleted. Redirects back to the originating page (/credentials or /oauth-brokers).

Stale account cleanup

discover_accounts() now tracks seen account and credential IDs during sync and deletes any rows not returned by Pipedream (revokes from Pipedream, removes vault + toolkit credential bindings).

Credentials page improvements

  • Shows app_slug, account_id, synced_at for OAuth credentials
  • "OAuth via Pipedream" badge
  • Reconnect replaces Edit for Pipedream OAuth credentials (edit form is not meaningful for these)
  • Delete calls deleteAccount (broker-aware revoke) instead of simple credential delete

Other fixes

  • build_absolute_url prefers X-Forwarded-Host (fixes dev proxy sending wrong Host header)
  • Vite dev proxy exempts /connect-callback paths from SPA HTML interception
  • spa_middleware excludes /oauth-brokers/.../connect-callback from SPA interception
  • Auth middleware exempts /oauth-brokers/.../connect-callback (browser redirect target, no auth token available)
  • Delete endpoint now uses account_id as path param (not api_host) — correct for multi-account same-app
  • Removed hardcoded healthy badge from broker page
  • external_user_id removed from ConnectLinkRequest API surface; derived from broker config

PRs superseded

This PR supersedes #154 (fix/credential-label-resync), which was an earlier partial fix for the label issue.

root added 30 commits April 2, 2026 17:58
Previously, labels were stored in oauth_broker_connect_labels at connect-link
creation time, keyed by (broker_id, external_user_id, app_slug). This meant
two connect-links for the same app (e.g. personal + work Gmail) would overwrite
each other before either OAuth flow completed.

New approach:
- Label is encoded as a query param in the success_redirect_uri passed to
  Pipedream when creating the connect token.
- Pipedream redirects the user's browser to
  /oauth-brokers/{id}/connect-callback?label=...&app=...&external_user_id=...
  after successful OAuth.
- The callback writes the label to oauth_broker_connect_labels at that point
  (one completion at a time) and immediately triggers discover_accounts() so
  the credential lands before the UI loads.
- Browser is then redirected to /credentials.

Because each connect-link gets its own token and its own success_redirect_uri,
the label is tied to the specific OAuth completion event — not to a shared
app_slug slot that can be clobbered by a concurrent link.

Changes:
- pipedream.py: create_connect_token() accepts optional success_redirect_uri
- oauth_brokers.py: connect-link handler builds redirect URI + new GET
  /connect-callback endpoint
- auth.py: connect-callback is public (browser redirect, no auth context)
- Add account_id to oauth_broker_accounts unique key + primary key.
  Old: UNIQUE(broker_id, external_user_id, api_host) — one slot per api_host,
  second Gmail account silently overwrote the first.
  New: UNIQUE(broker_id, external_user_id, api_host, account_id).

- Pending label (from connect-link callback) now only applies to NEW rows.
  Existing accounts keep their stored label — connecting a second Gmail
  no longer relabels the first one.

- credentials UPDATE on sync no longer touches label at all (only value/api_id).
  Label is set at INSERT time from pending, or preserved on subsequent syncs.

- oauth_brokers list endpoint now returns label field.

Migration 0002 recreates oauth_broker_accounts with new schema and migrates
existing rows (rebuilding id to include account_id).
…Auth, replaces old account after sync confirms new one
… delete logic (revoke Pipedream + clean vault + DB)
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.

1 participant