Riddy21 / Riddy21/Friday_Budgeting_Pro

[Bug] refresh_connection does not use Plaid Update Mode — re-auth creates new connection instead of updating existing

Open
#335 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug enhancement
Dominant language
Python
Stars
1
Forks
1
PR merge metrics
No merged PRs in 30d

Description

What was broken

Root Cause 1: No true Plaid Update Mode

refresh_connection generated a generic new Link token by calling create_link_token() with no access_token parameter. This is identical to starting a fresh bank link — Plaid has no way to know which existing connection to re-authenticate, so the user would end up with a duplicate connection and the stale one would remain needs_reauth.

Related: issue #34 (TODO was tracked there but never resolved).

Root Cause 2: disconnect FK constraint failure

When a connection has linked bank_accounts and associated transactions / transaction_entries, deleting the bank_connections row raised a FOREIGN KEY constraint failed error because child rows were not cleaned up first. This made it impossible to remove a real connection via the MCP tool or UI without manual DB surgery.

Root Cause 3: No graceful re-auth path

There was no end-to-end flow for re-authenticating a needs_reauth connection while preserving history. The only option was disconnect (broken) + reconnect (creates duplicate).


What was fixed

Fix 1: True Plaid Update Mode in PlaidProvider.create_link_token

Added an optional access_token parameter to PlaidProvider.create_link_token(). When provided, the link token request omits products and passes access_token — this is the documented Plaid Update Mode pattern that lets a user re-authenticate an existing item in-place.

Fix 2: refresh_connection now fetches and decrypts the stored token

refresh_connection(id) now:

  1. Looks up the connection row in the DB
  2. Decrypts the stored plaid_access_token_encrypted
  3. Calls provider.create_link_token(access_token=<decrypted>) for true Update Mode
  4. Embeds connection_id in the returned URL so /link/complete knows to update the existing row
Fix 3: complete_link Update Mode

Added optional connection_id parameter to complete_link(). When present:

  • Exchanges the public token (Plaid returns the same access token after re-auth)
  • Updates the existing bank_connections row (status='active', refreshed token)
  • Does not insert a duplicate row
Fix 4: Cascade delete in disconnect

disconnect() now deletes child rows in FK order before removing the connection:
transaction_entriestransactionsbank_accountssync_cursorsbank_connections
All wrapped in a single transaction for atomicity.

Fix 5: UI flow end-to-end
  • /link route now accepts optional connection_id query param
  • link.html injects connection_id as a hidden form field when present
  • /link/complete reads connection_id from the POST body and passes it to complete_link()

Files changed

  • server/providers/plaid.py — Update Mode support in create_link_token
  • server/main.pyrefresh_connection, complete_link, disconnect
  • ui/server.py/link route and /link/complete endpoint
  • ui/templates/link.html — hidden connection_id field
  • tests/test_bank_tools.py — new tests for Update Mode, cascade delete, and unknown-id error

All existing tests pass (pre-existing unrelated failure in test_ledger_mcp_tools.py unchanged).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading server/providers/plaid.py and the refresh_connection, complete_link, and disconnect paths in server/main.py. Then inspect ui/server.py, ui/templates/link.html, and tests/test_bank_tools.py to understand the end-to-end flow. Done means Update Mode preserves the existing connection, disconnect removes dependent rows atomically, and the named tests pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, database, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.