Skip to content

Delete the deprecated POST /oauth/google route (#957) - #664

Merged
thomasluizon merged 1 commit into
mainfrom
fix/ticket-957-delete-oauth-google-route
Sep 29, 2026
Merged

thomasluizon merged 1 commit into
mainfrom
fix/ticket-957-delete-oauth-google-route

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

Closes #957.

api#660 moved the MCP authorize page's Google sign-in to an authorization-code redirect through /oauth/google/start and /oauth/google/callback. It kept POST /oauth/google (the old One Tap tokeninfo path) only so the OpenAPI contract gate would keep passing. This deletes it.

What changed

  • POST /oauth/google, the OAuthController.GoogleAuthRequest record and FindOrCreateGoogleUserAsync are gone from src/Orbit.Api/Controllers/OAuthController.cs.
  • The two constructor dependencies that only that route used, IGenericRepository<User> and IHttpClientFactory, are gone with it, along with the imports they orphaned.
  • OAuthController.GoogleAuth is removed from the agent catalog in AgentCatalogService.Capabilities.cs.
  • ErrorCodes.GoogleTokenAudienceMismatch and ErrorMessages.GoogleTokenAudienceMismatch are removed: the deleted route was their only call site.
  • src/Orbit.Api/openapi.json is regenerated. Only the /oauth/google path entry disappears. The GoogleAuthRequest component schema stays, because the generator binds that name to AuthController.GoogleAuthRequest, which /api/Auth/google still uses.

No caller remains

  • src/Orbit.Api/OAuth/OAuthLoginPage.cs links to /oauth/google/start.
  • thomasluizon/orbit-ui-mobile on both origin/main and origin/redesign/main: no source hit for oauth/google or GOOGLE_TOKEN_AUDIENCE_MISMATCH. The only hit anywhere is a prose line in .claude/specs/orbit-prod-release.md that asks for this ticket.
  • No documented external client in this repository calls it.

Contract gate

The route already carried deprecated: true on main, so no extra record is needed. I built oasdiff v1.32.1, the version oasdiff/oasdiff-action/breaking@5e81b5c pins in its Dockerfile, and ran it against origin/main with this repository's .oasdiff.yaml:

$ oasdiff breaking origin/main:src/Orbit.Api/openapi.json src/Orbit.Api/openapi.json --fail-on ERR
No breaking changes to report, but the specs are different.
EXIT=0

$ oasdiff changelog origin/main:src/Orbit.Api/openapi.json src/Orbit.Api/openapi.json
1 changes: 0 error, 0 warning, 1 info
info	[api-path-removed-with-deprecation] in API POST /oauth/google

api-path-removed-with-deprecation is INFO in checker/rules.go, and the job sets fail-on: ERR, so the OpenAPI Breaking-Change Gate passes.

Test evidence

This is a deletion, not a defect fix, so there is no defect to reproduce. The closest equivalent applies: an existing test asserted the opposite of the acceptance criterion, and I ran it unchanged against the deletion before I touched it.

  1. Existing test unchanged, route deleted. dotnet test tests/Orbit.Infrastructure.Tests --no-build --filter "FullyQualifiedName~OAuthControllerTests.GoogleAuth_RouteRemainsAvailableAndDeprecated" fails for the intended reason:
Failed OAuthControllerTests.GoogleAuth_RouteRemainsAvailableAndDeprecated
  Expected route not to be <null>.
Failed!  - Failed: 1, Passed: 0, Total: 1
  1. Test inverted to assert the removal. GoogleAuth_OneTapRoute_IsGoneSoAPostTo_OauthGoogle_Returns404 scans every HttpMethodAttribute template on every ControllerBase in the Orbit.Api assembly, asserts /oauth/google is absent, and asserts the two replacement routes are still present, so a rename cannot make it pass by accident. Nothing serves /oauth/google any more, so a POST to it falls through to a 404.

  2. After the change, the whole suite passes. dotnet test Orbit.slnx --no-build:

Orbit.Analyzers.Tests        Passed: 32,   Failed: 0
Orbit.Domain.Tests           Passed: 630,  Failed: 0
Orbit.Application.Tests      Passed: 3716, Failed: 0
Orbit.Infrastructure.Tests   Passed: 2599, Failed: 0

node tools/check-suppression-allowlist.mjs, node tools/check-timeless.mjs --base origin/main, node tools/check-dashes.mjs, node tools/check-root-allowlist.mjs and node tools/arch-map.mjs all exit 0.

Assumptions

  • The acceptance criterion "the route returns 404" is proven by a reflection test over route templates, not by a live request, because a worker must never start a server. Rejected alternative: an integration test that boots the app, which this repository also forbids.
  • ErrorCodes.GoogleTokenAudienceMismatch is deleted rather than kept. The deleted route was its only producer and no consumer references the literal GOOGLE_TOKEN_AUDIENCE_MISMATCH in either repository. Rejected alternative: keeping a dead error code for a route that no longer exists.
  • #pragma warning disable S107 on the controller stays, even though the constructor now takes 7 parameters. I could not read Sonar's threshold from a real source, and SonarCloud Code Analysis is a required check, so I left a suppression that may still be live rather than remove it on memory. Rejected alternative: deleting the pragma and its tools/suppression-allowlist.json entry.
  • gating-matrix.json is left untouched. Running the generator locally rewrote only its provenance hash and input-file count, which is local-environment drift and not a product of this change. Rejected alternative: committing that churn.
  • The pre-existing unused using System.Security.Claims; in OAuthControllerTests.cs stays. It was already unused on main, so this change did not orphan it. Rejected alternative: an unrelated cleanup in a deletion PR.

🤖 Generated with Claude Code

The MCP authorize page moved to the authorization-code redirect through
/oauth/google/start and /oauth/google/callback, so the One Tap tokeninfo
route kept no caller in either app or in any documented client. It also
created users through a raw repository call instead of an application
command.

Remove the route, its GoogleAuthRequest record, FindOrCreateGoogleUserAsync,
the now orphaned user repository and HTTP client factory dependencies, the
OAuthController.GoogleAuth catalog entry and the GoogleTokenAudienceMismatch
error, and regenerate openapi.json. The route stayed deprecated on main, so
oasdiff reports the removal as api-path-removed-with-deprecation at INFO and
the breaking-change gate passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes The deprecated MCP One Tap endpoint deletion, its contract and catalog cleanup, and the replacement route assertions were reviewed against the remaining OAuth flow and the sibling consumer.

  • Route removal: POST /oauth/google and its tokeninfo user provisioning path are deleted; /oauth/google/start and /oauth/google/callback remain available to the MCP authorize page.
  • Contract cleanup: OpenAPI removes the deprecated path while the separate /api/auth/google request schema remains; the now-unused audience mismatch declarations and catalog entry are removed.
  • Test coverage: The controller test asserts the removed route template is absent across controllers and both replacement templates remain. The sibling shared endpoints target /api/auth/google, not the deleted route.

Pullfrog  | View workflow run | Using GPT Sol | 𝕏

@sonarqubecloud

Copy link
Copy Markdown

@thomasluizon
thomasluizon merged commit e596ee1 into main Sep 29, 2026
26 checks passed
@thomasluizon
thomasluizon deleted the fix/ticket-957-delete-oauth-google-route branch September 29, 2026 18:27
thomasluizon added a commit that referenced this pull request Sep 30, 2026
* Delete the deprecated POST /oauth/google route (#957) (#664)

The MCP authorize page moved to the authorization-code redirect through
/oauth/google/start and /oauth/google/callback, so the One Tap tokeninfo
route kept no caller in either app or in any documented client. It also
created users through a raw repository call instead of an application
command.

Remove the route, its GoogleAuthRequest record, FindOrCreateGoogleUserAsync,
the now orphaned user repository and HTTP client factory dependencies, the
OAuthController.GoogleAuth catalog entry and the GoogleTokenAudienceMismatch
error, and regenerate openapi.json. The route stayed deprecated on main, so
oasdiff reports the removal as api-path-removed-with-deprecation at INFO and
the breaking-change gate passes.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit e596ee1)

* chore: run the Pullfrog reviewer on the Codex harness with gpt-6.1-sol (#668)

* chore: run the Pullfrog reviewer on the Codex harness with gpt-6.1-sol

The Codex step now sets PULLFROG_AGENT=codex and passes openai/gpt-6.1-sol
as a raw model specifier, because no published Pullfrog alias resolves to it
yet. The Claude fallback moves to claude-opus-5-5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: keep the Pullfrog reviewer on the opencode harness at the model's default effort

The Codex CLI harness disables multi_agent, which drops the reviewfrog
specialist sub-agent, and a raw model specifier has no effort rung, so the
effort input was never applied. The review step keeps openai/gpt-6.1-sol on
the default opencode harness, where OpenAI's documented default effort is
medium.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit c95d548)

* Transfer or release a device's push subscription when it changes accounts (#932) (#667)

* Transfer a device's push subscription when another account registers it

A device keeps one push endpoint or FCM token across the accounts that
sign in on it. Before, a second account got PUSH_ENDPOINT_OWNED_BY_OTHER_USER,
the client rotated the endpoint, and the first account kept a dead row that
counted toward its five-device cap.

Now the row moves to the registering account when the request proves it
holds the device: a Web Push request must present the stored p256dh key
and auth secret, and an FCM request must present the stored token with
the FCM sentinel. A request without that proof still gets the old error,
so an account cannot take or remove another account's device.

Refs thomasluizon/orbit-tickets#932

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Let the account on a device release its prior account's push row

Unsubscribe only removed a row the caller owned. When a browser or
Android device stayed registered to a previous account and the account
now signed in turned push off, the client dropped the endpoint locally
while the previous account kept listing and counting a dead device.

Unsubscribe now takes the device's p256dh and auth, which both clients
already send, and removes another account's row only when they prove
control of the device, using the same check as the subscribe transfer.
Without that proof the row stays with its owner and the call still
returns success, so it does not reveal who owns an endpoint.

Refs thomasluizon/orbit-tickets#932

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: require explicit cross-account push subscription release

* fix: bind push unsubscribe deletion to the observed owner

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit ce78ccb)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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