refactor(P2-1): Extract pure Google OAuth helpers for web-wizard reuse#20
Merged
refactor(P2-1): Extract pure Google OAuth helpers for web-wizard reuse#20
Conversation
Prep work for the web-based auth wizard (Phase 2). Splits the Google OAuth dance in auth_setup.py into small pure building blocks so the interactive CLI (InstalledAppFlow + run_local_server) and the forthcoming web wizard (plain Flow + our own HTTP server) both go through the same scopes / client-config / token-exchange path without drift. New public functions (all sync, no I/O beyond the Flow itself): - build_google_client_config(client_id, client_secret) -> dict - build_google_flow(client_id, client_secret, redirect_uri) -> Flow (returns InstalledAppFlow when redirect_uri is None) - google_auth_url(flow) -> (auth_url, state) forces access_type=offline + prompt=consent so refresh_token is guaranteed on every authorization - exchange_google_code(flow, code) -> OAuthResult run_google_oauth now delegates to build_google_flow internally. Its public signature and return type are unchanged so the three places in test_auth_setup.py that monkey-patch it continue to work. 12 new tests in tests/test_auth_oauth_helpers.py covering: config shape, Flow vs InstalledAppFlow selection, scope coverage (Google Ads + Search Console), auth URL shape, refresh_token guarantee, missing-refresh fallback, and run_google_oauth regression. Full suite: 1672 passed, mypy (with --ignore-missing-imports per CI) / ruff / black clean. No behavior change for existing users.
- HIGH: _validate_local_redirect_uri() rejects non-localhost URIs in
build_google_flow so a caller cannot redirect OAuth grants to a
remote host. Enforces scheme=http + hostname in {127.0.0.1,
localhost}. 9 parametrized tests cover rejections and acceptances.
- HIGH: Add a comment on google_auth_url documenting the
prompt=consent injection-point asymmetry with run_google_oauth
(which injects via run_local_server).
- MEDIUM: Type build_google_flow return as Flow (was Any); same for
google_auth_url and exchange_google_code parameters. Top-level
import of Flow (was function-local, inconsistent with
InstalledAppFlow).
- MEDIUM: exchange_google_code RuntimeError now explains the fix
(re-run with prompt=consent + access_type=offline, or revoke at
https://myaccount.google.com/permissions).
- LOW: 3 new tests — fetch_token propagates unexpected errors,
error message is actionable (mentions both prompt/offline),
client_config dict is independent across calls (not shared state).
Full suite: 1684 passed, mypy / ruff / black clean.
This was referenced Apr 19, 2026
hyoshi
added a commit
that referenced
this pull request
May 1, 2026
…e EVERY commit (#76) Codifies the rule reinforced after PR #20 (OAuth helper, 2 HIGH issues found post-hoc) and PR #75 fixup (review-response commit pushed without re-review). Fixup, lint, and review-response commits all require their own review pass — a previous review on the same branch does NOT exempt follow-up commits. Exceptions (visual review only): docs/README/CHANGELOG, version bumps, Dependabot Actions bumps, typo fixes in comments.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First of 4 PRs implementing Phase 2 (web-based OAuth wizard so non-technical users never touch a terminal). This PR is prep-only — no user-facing behavior change. Extracts the Google OAuth dance in
auth_setup.pyinto small pure building blocks so the upcoming web wizard and the existing interactive CLI share the same scopes / client-config / token-exchange path.What changes
New public helpers in
mureo/auth_setup.pybuild_google_client_config(client_id, client_secret) -> dictbuild_google_flow(client_id, client_secret, redirect_uri=None)— returnsInstalledAppFlowwhenredirect_uri=None(current CLI path); returns plainFlowotherwise (web-wizard path).google_auth_url(flow) -> (auth_url, state)— forcesaccess_type=offline + prompt=consentsorefresh_tokenis guaranteed on every authorization, not just first grant.exchange_google_code(flow, code) -> OAuthResult— raisesRuntimeErrorif the response lacksrefresh_token.Existing
run_google_oauthrefactored to usebuild_google_flowPublic signature and return type unchanged. The three existing monkey-patches in
tests/test_auth_setup.pycontinue to work because they patch the outer function.Test plan
tests/test_auth_oauth_helpers.py: client-config shape, Flow vs InstalledAppFlow selection byredirect_uri, scope coverage (ads + webmasters), auth URL shape, refresh_token guarantee, and a regression lock onrun_google_oauth.mypy(CI config--ignore-missing-imports),ruff, andblackall clean.Roadmap
mureo auth setup --webflag