Skip to content

fix(bitbucket): send redirect_uri on the authorize hop - #139

Merged
jhd3197 merged 1 commit into
jhd3197:devfrom
MaxFreedomPollard:fix/bitbucket-authorize-redirect-uri
Sep 8, 2026
Merged

jhd3197 merged 1 commit into
jhd3197:devfrom
MaxFreedomPollard:fix/bitbucket-authorize-redirect-uri

Conversation

@MaxFreedomPollard

Copy link
Copy Markdown
Contributor

Description

SourceConnectionService.generate_bitbucket_authorize_url accepts a redirect_uri and never puts it in the authorize URL, so Bitbucket returns the operator to whatever callback is registered on the OAuth consumer instead of the panel origin the connect flow started from.

The cause is the params dict at backend/app/services/source_connection_service.py:613, which built the query string from client_id, response_type and state only. The two siblings in the same file both send the caller's value: generate_github_authorize_url at line 189 (line 207) and generate_gitlab_authorize_url at line 436 (line 446), and so does generate_auth_url in backend/app/services/sso_service.py:104 (line 134). Bitbucket was the only one of the four authorize-URL builders in the backend that dropped it.

Nothing else in the flow treats the value as optional. GET /source-connections/bitbucket/authorize answers 400 without it (backend/app/api/source_connections.py:319), and complete_bitbucket_callback posts that same redirect_uri to Bitbucket's token endpoint (backend/app/services/source_connection_service.py:649), so the authorize hop and the token hop currently name different callbacks. The panel derives the value from window.location.origin (frontend/src/components/settings/connections/ConnectionsHub.jsx:108), so the callback it asks for is the origin the operator actually reached the panel at.

The fix adds 'redirect_uri': redirect_uri to the params dict so the Bitbucket authorize hop asks for the callback its own token exchange later claims.

Related Issues

None.

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • 🚀 New feature (non-breaking change which adds functionality)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📝 Documentation update
  • 🎨 UI/UX improvement

How Has This Been Tested?

backend/tests/test_source_connection_oauth.py is a new parametrised test asserting that every provider's authorize URL carries the caller's redirect_uri, its client_id and its returned state. It is parametrised rather than written three times so a fourth provider is held to the same contract.

Run against an unmodified dev checkout it isolates the bug to Bitbucket: python -m pytest tests/test_source_connection_oauth.py -v gives 1 failed, 2 passed, with the GitHub and GitLab cases green and the Bitbucket case failing on KeyError: 'redirect_uri'.

With the fix, on macOS with Python 3.11.15 and backend/requirements.txt plus pytest/pytest-split:

  • python -m pytest tests/test_source_connection_oauth.py tests/test_github_app_manifest.py -q gives 11 passed.
  • python -m pytest tests --splits 4 --group 4 -q, the Backend CI shard that collects the new test, gives 1370 passed, 50 skipped.
  • python -m pytest tests --splits 4 --group 3 -q gives 1176 passed, 1 failed, 35 skipped. The failure is tests/test_probe_honesty_metrics.py::test_one_failed_section_does_not_blank_the_others, which fails identically on an unmodified dev checkout on this machine because the CPU probe is unavailable here.
  • python -m pytest tests/test_api_surface_inventory.py tests/test_route_authz_static.py tests/test_auth_decorator_boundaries.py tests/test_api_controller_boundaries.py tests/test_api_error_shape.py tests/test_redaction_one_door.py -q gives 28 passed.
  • python tests/check_test_count.py reports collected=5095 baseline=5089, above the floor.
  • python -m py_compile app/services/source_connection_service.py tests/test_source_connection_oauth.py passes.

No route was added or removed, so docs/API_SURFACE.md is unchanged.

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

generate_bitbucket_authorize_url() takes a redirect_uri and leaves it
out of the query string, so Bitbucket returns the user to whatever
callback is registered on the OAuth consumer rather than the one the
panel asked for. complete_bitbucket_callback() then posts that same
redirect_uri to the token endpoint, so the two halves of one flow name
different callbacks.

Both sibling generators in the same file send it
(generate_github_authorize_url line 207, generate_gitlab_authorize_url
line 446), as does generate_auth_url in sso_service.py; Bitbucket was
the only one that dropped it. GET /source-connections/bitbucket/authorize
already rejects a request without redirect_uri, so the value was always
present and always discarded.

Adds the parameter, plus a regression test parametrised over all three
providers so the next one added is held to the same contract.
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.

2 participants