Skip to content

Keep a custom OAuth login open and print its link when unattended - #1254

Open
yaoharry wants to merge 5 commits into
databricks:mainfrom
yaoharry:harry/unattended-custom-oauth-login
Open

yaoharry wants to merge 5 commits into
databricks:mainfrom
yaoharry:harry/unattended-custom-oauth-login

Conversation

@yaoharry

Copy link
Copy Markdown
Contributor

Stacked on #1253, which adds UG_NONINTERACTIVE (ucode.env.unattended_terminal()). Review only the last commit, "Keep a custom OAuth login open and print its link when unattended". The other two commits are #1253 and drop out once it merges. This overlaps #1251 on the same run(login_args, …) line; whichever merges second needs a trivial rebase.

Summary

ensure_custom_oauth_cli_token runs databricks auth login for the custom-client profile with a 180 s budget (CUSTOM_OAUTH_TIMEOUT_MS). Sometimes a wrapper relaunches ug claude in a terminal nobody watches, for example a coding-agent host restarting a session while the person is away. If the profile's grant has expired by then, the run fails like this:

  • the CLI opens a browser on a screen nobody is looking at, and prints nothing to the terminal;
  • nobody finishes consent within 180 s, so TimeoutExpired ends ug;
  • the agent never starts, and the wrapper only sees a terminal that exited, with no link to offer the person.

With UG_NONINTERACTIVE set:

  • The link is printed. The login runs with BROWSER=none, so the Databricks CLI prints the link instead of opening a browser: Please complete authentication by opening this link in your browser: followed by the …/oidc/v1/authorize?… URL. A wrapper can lift that link from the terminal and show it to the person.
  • The login stays open for 30 minutes (UNATTENDED_CUSTOM_OAUTH_LOGIN_TIMEOUT_S), so the person can finish it when they return, and the launch then continues.

Attended runs are unchanged: same 180 s budget, and the browser still opens.

Test plan

  • tests/test_custom_oauth.py::TestCustomOAuthCliLoginBudget (new):
    • With the variable unset, the login runs with env=None and timeout=180.
    • With UG_NONINTERACTIVE=1 and an existing BROWSER, the login runs with BROWSER=none, keeps the rest of the environment, and gets the 30-minute budget.
  • Manual, Databricks CLI v1.20.0: BROWSER=none databricks auth login --host <workspace> --profile <scratch> --client-id <id> --scopes all-apis printed the link line above, while the same command without BROWSER printed nothing on a machine whose opener works. Run against a scratch DATABRICKS_CONFIG_FILE and a dummy client id; consent was not completed.
  • uv run pytest tests/test_custom_oauth.py tests/test_databricks.py tests/test_managed_files.py: 560 passed, 7 failed. The same 7 TestCustomClientToken cases fail identically on origin/main on this machine (its environment supplies an unrelated bearer token).
  • uv run ruff check src tests, ruff format --check, ty check src/: clean.

This pull request and its description were written by Isaac.

Harry Yao and others added 5 commits October 22, 2026 21:28
A wrapper can start ug in a terminal nobody watches, e.g. a coding-agent host relaunching `ug claude` in tmux after a restart. stdin is a TTY there, so ug decided sudo may prompt and printed "Enter password to configure settings for Claude Code." followed by sudo's Password: prompt. The launch then blocked until the wrapper gave up.

UG_NONINTERACTIVE=1 now makes _sudo_may_prompt() false, so the run takes the existing no-terminal path: sudo -n repairs, a compatible launch continues with local settings, and a conflicting stale managed file still fails with the usual message.

Signed-off-by: Harry Yao <harry.yao@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Move the flag into ucode.env as unattended_terminal() so other prompts (the custom OAuth browser sign-in) can honor the same switch.

Signed-off-by: Harry Yao <harry.yao@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
ensure_custom_oauth_cli_token runs `databricks auth login` for the custom-client profile with a 180 s budget. In a terminal nobody watches (a wrapper relaunching `ug claude` after a restart), the CLI opens a browser the person can't see, nobody finishes consent, and the launch dies with TimeoutExpired. The agent then exits, and the wrapper only sees a dead terminal.

With UG_NONINTERACTIVE set, run the login with BROWSER=none so the CLI prints the sign-in link where the wrapper can surface it, and keep the login open for 30 minutes so the person can finish it when they return. Attended runs are unchanged.

Signed-off-by: Harry Yao <harry.yao@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
README conflict only: keep both the --user note from main and the UG_NONINTERACTIVE paragraph.

Signed-off-by: Harry Yao <harry.yao@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
…to the unattended login branch

Signed-off-by: Harry Yao <harry.yao@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.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