From e27c8a2bd63f4b3e93e8e11b153bf041c16a9a1d Mon Sep 17 00:00:00 2001 From: "FanaticPythoner (Nathan Trudeau)" Date: Sun, 26 Apr 2026 12:26:19 -0400 Subject: [PATCH] Rewrite onboarding prose to a neutral voice --- .env.example | 2 +- Justfile | 12 ++++---- README.md | 4 +-- scripts/common.sh | 4 +-- scripts/doctor.sh | 4 +-- scripts/forge_auth.py | 37 ++++++++++++------------ scripts/forge_login.sh | 4 +-- scripts/git-credential-forge.py | 8 ++--- scripts/install-git-credential-helper.sh | 4 +-- scripts/next_steps.sh | 6 ++-- scripts/setup.sh | 6 ++-- scripts/uninstall.sh | 4 +-- tests/README.md | 14 ++++----- tests/test_doctor.sh | 4 +-- tests/test_forge_auth.py | 8 ++--- tests/test_forge_auth_integration.py | 18 ++++++------ 16 files changed, 69 insertions(+), 70 deletions(-) diff --git a/.env.example b/.env.example index 054fdb5..f99f5d5 100644 --- a/.env.example +++ b/.env.example @@ -23,7 +23,7 @@ FSDGG_CLI_CLIENT_ID="ba4ec9ec-8ae8-4450-9cec-fd532bbe63d5" # http-scheme loopback: http://127.0.0.1:/, # http://[::1]:/, or http://localhost:/. # The value must also match the redirect URI registered in the Gitea -# OAuth app; change it only if your Gitea app registration was updated +# OAuth app; change it only if the Gitea app registration was updated # in sync. FSDGG_CLI_REDIRECT_URI="http://127.0.0.1:38111/callback" diff --git a/Justfile b/Justfile index 65f26f2..5b99bca 100644 --- a/Justfile +++ b/Justfile @@ -53,7 +53,7 @@ check-gitea: @echo "[check-gitea] GET $FORGE_GITEA_URL/api/v1/version" @curl -fsS --max-time 10 ${FORGE_INSECURE_TLS:+-k} "$FORGE_GITEA_URL/api/v1/version" \ | python3 -c 'import json,sys; d=json.load(sys.stdin); print("[check-gitea] Gitea version:", d.get("version","?"))' - @echo "[check-gitea] OK -- you can reach Gitea." + @echo "[check-gitea] OK: Gitea reachable." # Browser OAuth2 (PKCE) login. Reuses a live token; runs the flow otherwise. login: @@ -91,12 +91,12 @@ check-access: DISPLAY='' WAYLAND_DISPLAY='' \ git ls-remote "$FORGE_ORCHESTRATOR_REPO_URL" HEAD >/dev/null 2>&1 \ && { \ - echo "[check-access] OK -- you can reach the orchestrator."; \ + echo "[check-access] OK: orchestrator reachable."; \ } || { \ echo "[check-access] FAILED. Likely causes:"; \ - echo " - you have not run 'just login' yet"; \ - echo " - your refresh token expired; run 'just relogin'"; \ - echo " - your Gitea account is not yet in the org"; \ + echo " - 'just login' has not completed yet"; \ + echo " - stored refresh token expired; run 'just relogin'"; \ + echo " - Gitea account is not yet in the org"; \ echo " - FORGE_ORCHESTRATOR_REPO_URL in .env is wrong"; \ exit 1; \ } @@ -141,7 +141,7 @@ next-steps *args: run-next-steps *args: @bash scripts/next_steps.sh --run {{args}} -# Remove the credential helper and our fields from client-auth.json. +# Remove the credential helper and repo-managed fields from client-auth.json. uninstall: @bash scripts/uninstall.sh diff --git a/README.md b/README.md index 83f8ea2..f21cc1a 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ edit is `FORGE_GITEA_USERNAME`. Defaults: **`FSDGG_CLI_REDIRECT_URI` is not the Gitea URL.** The Gitea server is at `FORGE_GITEA_URL` (remote, HTTPS). The redirect URI is the local -loopback HTTP listener the CLI binds on *your* machine so Gitea can +loopback HTTP listener the CLI binds on the local machine so Gitea can hand back the OAuth authorisation code; OAuth 2.0 for Native Apps (RFC 8252 §7.3) prohibits any non-loopback / non-HTTP scheme here, and no public CA will issue a cert for `127.0.0.1` so HTTPS on @@ -217,7 +217,7 @@ with a browser to populate a valid refresh token before running | Git prompts for a password on pull/push | Refresh token expired. Run `just relogin`. | | `just status` shows `live: False` | Run `just refresh`; also happens automatically on the next git op. | | `just clone-orchestrator` prints `already cloned` | Intended; idempotent. | -| Want a clean slate | `just uninstall`. | +| Reset local state | `just uninstall`. | ## Security properties diff --git a/scripts/common.sh b/scripts/common.sh index 2143ef0..1f9c063 100755 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -63,7 +63,7 @@ repo_root() { } # Load $repo/.env into the environment. -# Existing environment values take precedence (matches just's dotenv-load). +# Existing environment values take precedence (matches dotenv-load in just). load_env() { local root env line key rest root="$(repo_root)" @@ -111,6 +111,6 @@ require_cmd() { require_env() { local name="$1" if [ -z "${!name:-}" ]; then - die "environment variable '$name' is unset. Run 'just init-env' and fill .env." + die "environment variable '$name' is unset. Run 'just init-env'. Fill .env." fi } diff --git a/scripts/doctor.sh b/scripts/doctor.sh index 28cc036..0d73eed 100755 --- a/scripts/doctor.sh +++ b/scripts/doctor.sh @@ -110,7 +110,7 @@ if command -v xdg-open >/dev/null 2>&1 || command -v open >/dev/null 2>&1; then printf ' %s %-12s -> (detected)\n' "$(_fc_tag "$_FC_GREEN" ok)" 'web browser' else printf ' %s %-12s -> no xdg-open/open on PATH.\n' "$(_fc_tag "$_FC_YELLOW" warn)" 'web browser' - printf ' if this is a headless machine, run: just login-headless\n' + printf ' headless mode: just login-headless\n' case "$pm" in apt) printf ' otherwise install: sudo apt-get install -y xdg-utils\n' ;; dnf) printf ' otherwise install: sudo dnf install -y xdg-utils\n' ;; @@ -214,7 +214,7 @@ if [ "$missing" -gt 0 ]; then printf ' %s\n' "$c" done printf '\n' - warn 'then re-run: just doctor' + warn 're-run: just doctor' exit 1 fi diff --git a/scripts/forge_auth.py b/scripts/forge_auth.py index d5d0b89..fe217e4 100755 --- a/scripts/forge_auth.py +++ b/scripts/forge_auth.py @@ -282,11 +282,12 @@ def _http_post_form( def discover_endpoints(config: ForgeAuthConfig) -> dict[str, str]: """Call Gitea's ``/.well-known/openid-configuration`` and return the - three endpoints we care about, validated against the issuer. + three required endpoints, validated against the issuer. """ url = f"{config.gitea_base_url}/.well-known/openid-configuration" payload = _http_get_json(url, insecure_tls=config.insecure_tls) issuer = payload.get("issuer") + if not isinstance(issuer, str) or issuer.rstrip("/") != config.gitea_base_url.rstrip("/"): raise AuthError( f"OIDC discovery issuer {issuer!r} does not match " @@ -401,7 +402,7 @@ class _CallbackHandler(BaseHTTPRequestHandler): self.send_header("Content-Type", "text/html; charset=utf-8") self.end_headers() self.wfile.write( - b"

Login failed

You can close this window.

" + b"

Login failed

Window close is safe.

" ) server.result_queue.put( _build_authorize_error( @@ -426,7 +427,7 @@ class _CallbackHandler(BaseHTTPRequestHandler): self.wfile.write( b"" b"

Login complete.

" - b"

You can close this window and return to your terminal.

" + b"

Window close is safe. Terminal focus can resume.

" b"" ) server.result_queue.put((code, state)) @@ -471,7 +472,7 @@ def _build_authorize_error( gitea_base_url : str Base URL of the Gitea server. Used to build the user-settings URL in the remediation message. Empty string yields a - ```` placeholder. + ```` placeholder. client_id : str, optional OAuth client id requesting the authorization. Surfaced in the "different scope" message to disambiguate the grant row in @@ -493,7 +494,7 @@ def _build_authorize_error( settings_url = ( f"{base}/user/settings/applications" if base - else "/user/settings/applications" + else "/user/settings/applications" ) if "different scope" in low or ("scope" in low and "grant" in low): @@ -554,8 +555,8 @@ def wait_for_callback( except OSError as exc: raise AuthError( f"cannot bind loopback server on {host}:{port}: {exc}. " - f"Is another 'just login' still running? " - f"If port {port} is held by an unrelated process, override " + f"Another 'just login' instance may still be running. " + f"If port {port} is held by an unrelated process, set " f"FSDGG_CLI_REDIRECT_URI in .env (note: the port must match " f"the OAuth app registered in Gitea)." ) from exc @@ -566,7 +567,7 @@ def wait_for_callback( except Exception as exc: raise AuthError( f"timed out after {int(timeout_seconds)}s waiting for OAuth " - f"callback. Did you complete the browser login?" + f"callback. Browser login completion was not detected." ) from exc finally: server.server_close() @@ -763,18 +764,17 @@ def _print_headless_guidance(auth_url: str, redirect) -> None: ] if in_ssh: lines += [ - f"{info_tag} this process is running inside an SSH session. From the", - " machine where you will open the browser, run:", + f"{info_tag} SSH session detected. From the browser-side machine run:", "", f" ssh -L {cb_port}:127.0.0.1:{cb_port} {user}@{hostname}", "", - " and paste the URL above into THAT machine's browser.", + " Paste the URL above into that machine's browser.", "", ] else: lines += [ - f"{info_tag} if this machine is remote, from the machine with the", - " browser run (before pasting the URL):", + f"{info_tag} Remote-host case: from the browser-side machine run", + " the following command before pasting the URL:", "", f" ssh -L {cb_port}:127.0.0.1:{cb_port} {user}@{hostname}", "", @@ -825,8 +825,8 @@ def run_login( if print_authorize_url: if open_browser: cli_info( - f"open this URL in your browser if it does not open " - f"automatically:\n {auth_url}" + f"authorization URL (fallback when automatic browser open fails):\n" + f" {auth_url}" ) else: _print_headless_guidance(auth_url, redirect) @@ -882,7 +882,7 @@ def run_login( f"'{config.expected_username}'. The stored auth file has " "NOT been updated.\n" " To fix:\n" - f" 1. Sign out of Gitea in your browser: {logout_url}\n" + f" 1. Sign out of Gitea in the browser: {logout_url}\n" f" 2. Sign back in as '{config.expected_username}'\n" " 3. Re-run 'just login'" ) @@ -905,8 +905,7 @@ def run_refresh(config: ForgeAuthConfig, *, must_refresh: bool = False) -> AuthF If ``must_refresh`` is False and the access token is still live, this is a no-op. If refresh fails, raises ``AuthError`` (the credential helper surfaces this to git as "fall through to prompt", - and the CLI ``just refresh`` surfaces it with an instruction to - re-run ``just login``). + and the CLI ``just refresh`` surfaces the failure to stderr). """ store = auth_store_path() existing = AuthFile.read(store) @@ -934,7 +933,7 @@ def run_refresh(config: ForgeAuthConfig, *, must_refresh: bool = False) -> AuthF def run_logout() -> Path | None: """Remove every codevalet-managed field from the auth file. - If the file only ever held our fields, delete it entirely. If the + If the file only ever held repo-managed fields, delete it entirely. If the gateway has already populated its own fields (access_token etc.), wipe only the Gitea + welcome-repo keys and leave the rest. """ diff --git a/scripts/forge_login.sh b/scripts/forge_login.sh index 0b9f427..20437c9 100755 --- a/scripts/forge_login.sh +++ b/scripts/forge_login.sh @@ -24,6 +24,6 @@ python3 "$here/forge_auth.py" login "$@" "$here/install-git-credential-helper.sh" ok "login complete" -note "git operations against your Gitea server now authenticate silently." -note "Test with:" +note "git operations against FORGE_GITEA_URL now authenticate without prompts." +note "Verification command:" note " git ls-remote \"\$FORGE_ORCHESTRATOR_REPO_URL\"" diff --git a/scripts/git-credential-forge.py b/scripts/git-credential-forge.py index f6138f0..1e73071 100755 --- a/scripts/git-credential-forge.py +++ b/scripts/git-credential-forge.py @@ -16,7 +16,7 @@ On every ``get`` the helper: unchanged so the normal prompt chain continues. 2. Checks that the git request host matches the host recorded in ``_forge_gitea_base_url`` (or ``FORGE_GITEA_URL``). If not, passes - through: this ensures we never hand OAuth tokens to unrelated + through: this prevents OAuth token disclosure to unrelated hosts even if git mis-scopes its lookup. 3. If ``gitea_access_token`` is live, emits ``username=`` and ``password=``. @@ -29,7 +29,7 @@ Security notes -------------- * The helper never writes to stdout except the credential key=value block. Logs go to stderr. -* On OAuth refresh failure we exit **non-zero** rather than silently +* On OAuth refresh failure the process exits **non-zero** rather than silently returning stale credentials. """ from __future__ import annotations @@ -93,7 +93,7 @@ def _configured_host() -> tuple[str, str, int | None] | None: Priority: 1. The ``_forge_gitea_base_url`` field inside the stored auth - file: that is the host we actually authenticated against. + file: the authenticated host. 2. The ``FORGE_GITEA_URL`` env var (pre-login override). Returns None if neither is set; the helper then passes through. """ @@ -150,7 +150,7 @@ def _request_matches( def cmd_get(fields: dict[str, str]) -> int: configured = _configured_host() if configured is None: - emit(fields) # pass-through: we don't know who we answer for + emit(fields) # pass-through: helper scope is undefined return 0 if not _request_matches(fields, configured): emit(fields) # pass-through: request is for a different host diff --git a/scripts/install-git-credential-helper.sh b/scripts/install-git-credential-helper.sh index 22a4f8b..03bead7 100755 --- a/scripts/install-git-credential-helper.sh +++ b/scripts/install-git-credential-helper.sh @@ -37,11 +37,11 @@ python3 -c 'import sys; compile(open(sys.argv[1]).read(), sys.argv[1], "exec")' key="credential.${FORGE_GITEA_URL}.helper" use_path_key="credential.${FORGE_GITEA_URL}.useHttpPath" -# Wipe any previous helpers we may have set so we do not stack them. +# Wipe previously configured helpers to avoid stacking entries. git config --global --unset-all "$key" 2>/dev/null || true git config --global --unset-all "$use_path_key" 2>/dev/null || true -# Username is derived from the OAuth token at runtime by the helper; we +# Username is derived from the OAuth token at runtime by the helper; do # deliberately do NOT set credential..username here, to avoid # pinning an old login. The helper emits username= on every # `get`, which git respects. diff --git a/scripts/next_steps.sh b/scripts/next_steps.sh index a3029cd..1e9a0f8 100755 --- a/scripts/next_steps.sh +++ b/scripts/next_steps.sh @@ -66,7 +66,7 @@ orchestrator="$workspace_root/$repo_name" if [ ! -d "$orchestrator/.git" ]; then err "orchestrator checkout not found at $orchestrator" - note "run 'just clone-orchestrator' (or 'just setup') first." + note "required command: just clone-orchestrator" exit 1 fi @@ -119,9 +119,9 @@ if [ "$MODE" != "run" ]; then if [ "${#forward_args[@]}" -gt 0 ]; then fwd_suffix=" ${forward_args[*]}" fi - note "Execute via:" + note "Command:" note " just run-next-steps${fwd_suffix}" - note "Execute inside the orchestrator:" + note "Orchestrator command:" note " cd $orchestrator && just contributor-setup${fwd_suffix}" exit 0 fi diff --git a/scripts/setup.sh b/scripts/setup.sh index d6b098e..d12d886 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -84,7 +84,7 @@ fi load_env if [ -z "${FORGE_GITEA_USERNAME:-}" ]; then - note "FORGE_GITEA_USERNAME is blank. Enter your Gitea username exactly as it appears in your profile URL." + note "FORGE_GITEA_USERNAME is blank. Enter the Gitea username from the profile URL." new_user="$(prompt_line "Gitea username")" if [ -z "$new_user" ]; then die "no username entered: aborting. Edit .env by hand and re-run 'just setup'." @@ -180,7 +180,7 @@ if [ "$run_login" = "1" ]; then forbid any interaction. Choose one: 1. Drop FORGE_SETUP_YES and re-run 'just setup --headless' (setup will print the authorisation URL and wait - while you paste it into a browser). + while the URL is pasted into a browser). 2. Run 'just login' once on a machine with a browser to populate the stored refresh token, then re-run 'just setup --headless' from the headless host. @@ -202,7 +202,7 @@ GIT_TERMINAL_PROMPT=0 GCM_INTERACTIVE=Never \ VSCODE_GIT_ASKPASS_EXTRA_ARGS="" VSCODE_GIT_IPC_HANDLE="" \ DISPLAY="" WAYLAND_DISPLAY="" \ git ls-remote "$FORGE_ORCHESTRATOR_REPO_URL" HEAD >/dev/null 2>&1 \ - || die "git ls-remote failed. Your account may not yet be in the org, or FORGE_ORCHESTRATOR_REPO_URL is wrong." + || die "git ls-remote failed. Account membership may be missing, or FORGE_ORCHESTRATOR_REPO_URL is wrong." ok "silent clone access confirmed" step "6/7 cloning the orchestrator (just clone-orchestrator)" diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh index a8fdec4..d63f8e5 100755 --- a/scripts/uninstall.sh +++ b/scripts/uninstall.sh @@ -10,7 +10,7 @@ here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" load_env -# 1. Log out at the OAuth layer (clears our managed fields from +# 1. Log out at the OAuth layer (clears repo-managed fields from # client-auth.json and keeps any fields owned by the orchestrator # gateway untouched). if [ -f "$here/forge_auth.py" ]; then @@ -37,4 +37,4 @@ done info 'uninstall complete.' info "note: $FORGE_AUTH_FILE is left in place if the orchestrator wrote it;" -info ' delete it manually if you want a completely clean state.' +info ' delete it manually for a fully clean state.' diff --git a/tests/README.md b/tests/README.md index d948f6d..438ac3a 100644 --- a/tests/README.md +++ b/tests/README.md @@ -13,7 +13,7 @@ bash tests/test_doctor.sh ## Inventory -- **`test_forge_auth.py`** — `scripts/forge_auth.py` unit tests: PKCE +- **`test_forge_auth.py`**: `scripts/forge_auth.py` unit tests: PKCE pair generation, HMAC state signing + CSRF rejection, `ForgeAuthConfig.from_env` validation (loopback-only redirect, missing port, missing env vars, `FORGE_GITEA_USERNAME` @@ -22,19 +22,19 @@ bash tests/test_doctor.sh read/write/merge/`has_live_gitea_token`, `auth_store_path` precedence, `run_logout`, `main()` dispatcher. -- **`test_git_credential_forge.py`** — `scripts/git-credential-forge.py` +- **`test_git_credential_forge.py`**: `scripts/git-credential-forge.py` unit tests: credential protocol I/O, host/scheme/port matching, live-token fast-path, pass-through for missing store or non-matching host, expired-token refresh, refresh-failure handling, `store`/`erase` no-ops, `main()` dispatcher. -- **`test_forge_auth_integration.py`** — end-to-end Python integration +- **`test_forge_auth_integration.py`**: end-to-end Python integration tests against `tests/mock_oidc_server.py`: full PKCE flow, gateway-required schema on disk, idempotent re-login, refresh token rotation with server-side revocation, logout preserving gateway-bearer fields. -- **`test_forge_auth_integration.sh`** — shell end-to-end: drives +- **`test_forge_auth_integration.sh`**: shell end-to-end: drives `forge_auth.py login` against the mock server, installs the credential helper into a sandboxed `$HOME`, and exercises `git credential fill`. Covers URL matching, `github.com` @@ -43,17 +43,17 @@ bash tests/test_doctor.sh logout URL surfaced, authorise URL carries `prompt=login` + `login_hint`). -- **`test_setup_args.sh`** — `scripts/setup.sh` coverage: argument +- **`test_setup_args.sh`**: `scripts/setup.sh` coverage: argument parsing, `--help`, `--headless` wiring to `forge_login.sh --no-browser`, the `--headless + FORGE_SETUP_YES=1` hang guard, live-token reuse, silent-refresh rescue, `prompt_choice` non-tty stdout isolation. -- **`test_doctor.sh`** — `scripts/doctor.sh`: miss-path under a +- **`test_doctor.sh`**: `scripts/doctor.sh`: miss-path under a sandboxed PATH, asserts every `[MISS]` line is followed by a `fix:` line. -- **`mock_oidc_server.py`** — test fixture implementing +- **`mock_oidc_server.py`**: test fixture implementing `/.well-known/openid-configuration`, `/login/oauth/authorize`, `/login/oauth/access_token`, `/login/oauth/userinfo`. PKCE verification on `authorization_code`; rotation + revocation on diff --git a/tests/test_doctor.sh b/tests/test_doctor.sh index 78ab3f4..05beb5a 100755 --- a/tests/test_doctor.sh +++ b/tests/test_doctor.sh @@ -15,8 +15,8 @@ out="$(mktemp)" trap 'rm -f "$out"' EXIT # Strip everything that could satisfy the checks we want to fail. -# /usr/bin/python3 is 3.10.x on Ubuntu 22.04; that's fine: we want to -# prove the python>=3.11 miss branch renders its fix+alt lines. +# /usr/bin/python3 is 3.10.x on Ubuntu 22.04; this case exercises the +# python>=3.11 miss branch and its fix+alt lines. if env -i HOME="$HOME" PATH="/usr/bin:/bin" bash "$repo/scripts/doctor.sh" >"$out" 2>&1; then echo "FAIL: doctor.sh exited 0 despite missing prerequisites" cat "$out" diff --git a/tests/test_forge_auth.py b/tests/test_forge_auth.py index d010bcf..00f29dd 100755 --- a/tests/test_forge_auth.py +++ b/tests/test_forge_auth.py @@ -335,7 +335,7 @@ class AuthFileTests(unittest.TestCase): def test_merge_login_preserves_gateway_bearer(self) -> None: # Simulates the case where the orchestrator already ran - # `auth login` and populated the gateway bearer. We must not + # `auth login` and populated the gateway bearer. The code must not # overwrite those fields. f = fa.AuthFile(raw={ "username": "old-alice", @@ -396,8 +396,8 @@ class AuthFileTests(unittest.TestCase): self.assertEqual(roundtrip["username"], "u") def test_write_preserves_unknown_keys(self) -> None: - # Forward-compat: the gateway might add new fields we don't - # know about. Writing must preserve them verbatim. + # Forward-compat: the gateway might add new fields unknown to the + # current schema. Writing must preserve them verbatim. raw = {"username": "u", "future_field": {"x": 1}, "access_token": "A"} with tempfile.TemporaryDirectory() as d: p = Path(d) / "a.json" @@ -644,7 +644,7 @@ class BuildAuthorizeErrorTests(unittest.TestCase): def test_different_scope_without_base_url_uses_placeholder(self) -> None: self.assertIn( - "/user/settings/applications", + "/user/settings/applications", str(self._exc_different_scope(gitea_base_url="")), ) diff --git a/tests/test_forge_auth_integration.py b/tests/test_forge_auth_integration.py index 6a6782d..5d82b65 100755 --- a/tests/test_forge_auth_integration.py +++ b/tests/test_forge_auth_integration.py @@ -4,7 +4,7 @@ Covers the full PKCE login flow (authorize → callback → token exchange → userinfo → persist), transparent refresh, logout, and the idempotent "already authenticated" short-circuit. -No real network calls. No browser required: we simulate the +No real network calls. No browser required: the test simulates the browser by doing an HTTP GET to the authorize endpoint; the mock server 302-redirects to the loopback callback, which `forge_auth.run_login` is already listening on. @@ -44,14 +44,14 @@ def _free_loopback_port() -> int: class _MockBrowser: """Drive the authorize endpoint on a worker thread. - We wait a fraction of a second for `run_login` to bind its + The worker waits a fraction of a second for `run_login` to bind its loopback callback server, then GET the authorize URL. The mock - server redirects us to the callback; following the redirect + server redirects to the callback; following the redirect causes `run_login`'s callback handler to fire, and the auth flow completes. urllib's default opener follows redirects automatically, which is - exactly what we want here: one GET, one automatic redirect, done. + the required behavior here: one GET, one automatic redirect, done. """ def __init__(self, authorize_url: str, delay_seconds: float = 0.2) -> None: @@ -111,14 +111,14 @@ class ForgeAuthIntegrationTests(unittest.TestCase): # Helpers # ----------------------------------------------------------------- def _login(self) -> fa.AuthFile: - """Run run_login() with an auto-browser that does the GET for us.""" + """Run run_login() with an auto-browser issuing the authorize GET.""" with mock.patch.dict(os.environ, self.env, clear=True): config = fa.ForgeAuthConfig.from_env() - # We need to start the mock "browser" AFTER run_login + # The mock browser starts AFTER run_login # prints the authorize URL but BEFORE it blocks on the # loopback server. Since run_login prints then blocks - # synchronously, we can intercept webbrowser.open to + # synchronously, the code intercepts webbrowser.open to # kick off the GET at exactly the right moment. browser_holder: dict[str, _MockBrowser] = {} @@ -240,8 +240,8 @@ class ForgeAuthIntegrationTests(unittest.TestCase): def test_callback_state_csrf_mismatch_raises(self) -> None: """A tampered state on the callback must raise. - We cannot easily tamper with the real PKCE flow end-to-end, - so we exercise verify_state directly: the `run_login` path + The real PKCE flow is not easily tampered end-to-end here, + so the test exercises verify_state directly: the `run_login` path wires it straight through. """ key = b"\x01" * 32