Skip to content

Tell AI agents to pass the device code login on to the user - #7235

Open
maksim-zuev-db wants to merge 6 commits into
device-code-agent-loginfrom
device-code-agent-notes
Open

maksim-zuev-db wants to merge 6 commits into
device-code-agent-loginfrom
device-code-agent-notes

Conversation

@maksim-zuev-db

@maksim-zuev-db maksim-zuev-db commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Stacked on #7220.

Changes

When an AI agent is detected (useragent.AgentProvider()), device code login prints a note after the user code that tells the agent to give the URL and the code to the user, not to open the URL or approve the login itself, and to ask the user for the confirmation code. In the interactive login, the note also tells the agent to enter the code at the Confirmation code prompt. Agent detection does not change the flow.

The note uses the new agent.AgentNote, which AgentNotice now uses too, with its text unchanged. The new testutil.SetAgent keeps the agent running the tests out of them.

Why

The login must be approved by the user, but an agent that runs it gets the URL and the code and could try to approve it itself, or not pass it on. Split out of #7220 to keep that PR smaller.

Tests

The device-code/non-interactive/nominal acceptance test runs both steps with AGENT set. TestPromptForConfirmationCode_Agent checks the interactive note, and libs/agent tests cover AgentNote and AgentNotice.

This pull request and its description were written by Isaac.

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Commit: f52b31d16 | Workflow run

Regression test report

Selected 6 changed tests (limit=20, 0 not selected)

6 tests: regression=6 unreleased=0 coverage=0 failing=0

test branch main (aed6702) latest (v1.23.0)
TestAccept/cmd/auth/login/device-code/non-interactive/concurrent ✅ ❌ ❌
TestAccept/cmd/auth/login/device-code/non-interactive/errors ✅ ❌ ❌
TestAccept/cmd/auth/login/device-code/non-interactive/flags ✅ ❌ ❌
TestAccept/cmd/auth/login/device-code/non-interactive/nominal ✅ ❌ ❌
TestAccept/cmd/auth/login/device-code/unsupported-host ✅ ❌ ❌
TestAccept/cmd/auth/logout/pending-device-code-login ✅ ❌ ❌
TestAccept/cmd/auth/login/device-code/non-interactive/concurrent: branch ✅ | main (aed6702) ❌ | latest (v1.23.0) ❌

main (aed6702):

acceptance_test.go:1341: Diff:
        --- cmd/auth/login/device-code/non-interactive/concurrent/output.txt
        +++ /tmp/TestAcceptcmdauthlogindevice-codenon-interactiveconcurrent3504990806/002/output.txt
        @@ -3,58 +3,4 @@
         
         >>> [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code
         
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code
        -
        ->>> update_file.py home/.databricks/token-cache.json WDJBMJHT NEWRCODE
        -
        ->>> cache_keys
        -[
        -  "test#device-code"
        -]
        -
        ->>> musterr [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Error: a newer login for profile test replaced this one. Complete the newer login with its user code. Run the same command without --user-code and --confirmation-code to start a new login
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --user-code NEWR-CODE --confirmation-code FTFA-QQKY
        -Profile test was successfully saved
        -
        ->>> print_requests.py --oneline //oidc/v1/token
        -{"method": "POST", "path": "/oidc/v1/token", "raw_body": "client_id=databricks-cli&confirmation_code=FTFAQQKY&device_code=device-code-2&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code"}
        -
        -=== Logins for different targets are independent
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile a --use-device-code
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile b --use-device-code
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --use-device-code
        -
        ->>> cache_keys
        -[
        -  "a#device-code",
        -  "b#device-code",
        -  "[DATABRICKS_URL]",
        -  "[DATABRICKS_URL]#device-code",
        -  "test"
        -]
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile b --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Profile b was successfully saved
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile a --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Profile a was successfully saved
        -
        ->>> cache_keys
        -[
        -  "a",
        -  "b",
        -  "[DATABRICKS_URL]",
        -  "test"
        -]
        -
        ->>> print_requests.py --oneline //oidc/v1/token
        -{"method": "POST", "path": "/oidc/v1/token", "raw_body": "client_id=databricks-cli&confirmation_code=FTFAQQKY&device_code=device-code-4&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code"}
        -{"method": "POST", "path": "/oidc/v1/token", "raw_body": "client_id=databricks-cli&confirmation_code=FTFAQQKY&device_code=device-code-5&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code"}
        -{"method": "POST", "path": "/oidc/v1/token", "raw_body": "client_id=databricks-cli&confirmation_code=FTFAQQKY&device_code=device-code-3&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code"}
        +Exit code: 1
        
    acceptance_test.go:1119: 
        LOG.first: Using a browser on another device, visit:
        LOG.first: http://127.0.0.1:46073/device/verification
        LOG.first: 
        LOG.first: And enter the code:
        LOG.first: WDJB-MJHT
        LOG.first: 
        LOG.first: Once finished, enter the confirmation code provided in your browser.
        LOG.first: Confirmation code: Error: reading confirmation code: EOF

latest (v1.23.0):

acceptance_test.go:1341: Diff:
        --- cmd/auth/login/device-code/non-interactive/concurrent/output.txt
        +++ /tmp/TestAcceptcmdauthlogindevice-codenon-interactiveconcurrent1334818579/002/output.txt
        @@ -3,58 +3,4 @@
         
         >>> [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code
         
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code
        -
        ->>> update_file.py home/.databricks/token-cache.json WDJBMJHT NEWRCODE
        -
        ->>> cache_keys
        -[
        -  "test#device-code"
        -]
        -
        ->>> musterr [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Error: a newer login for profile test replaced this one. Complete the newer login with its user code. Run the same command without --user-code and --confirmation-code to start a new login
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --user-code NEWR-CODE --confirmation-code FTFA-QQKY
        -Profile test was successfully saved
        -
        ->>> print_requests.py --oneline //oidc/v1/token
        -{"method": "POST", "path": "/oidc/v1/token", "raw_body": "client_id=databricks-cli&confirmation_code=FTFAQQKY&device_code=device-code-2&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code"}
        -
        -=== Logins for different targets are independent
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile a --use-device-code
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile b --use-device-code
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --use-device-code
        -
        ->>> cache_keys
        -[
        -  "a#device-code",
        -  "b#device-code",
        -  "[DATABRICKS_URL]",
        -  "[DATABRICKS_URL]#device-code",
        -  "test"
        -]
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile b --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Profile b was successfully saved
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile a --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Profile a was successfully saved
        -
        ->>> cache_keys
        -[
        -  "a",
        -  "b",
        -  "[DATABRICKS_URL]",
        -  "test"
        -]
        -
        ->>> print_requests.py --oneline //oidc/v1/token
        -{"method": "POST", "path": "/oidc/v1/token", "raw_body": "client_id=databricks-cli&confirmation_code=FTFAQQKY&device_code=device-code-4&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code"}
        -{"method": "POST", "path": "/oidc/v1/token", "raw_body": "client_id=databricks-cli&confirmation_code=FTFAQQKY&device_code=device-code-5&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code"}
        -{"method": "POST", "path": "/oidc/v1/token", "raw_body": "client_id=databricks-cli&confirmation_code=FTFAQQKY&device_code=device-code-3&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code"}
        +Exit code: 1
        
    acceptance_test.go:1119: 
        LOG.first: Using a browser on another device, visit:
        LOG.first: http://127.0.0.1:40803/device/verification
        LOG.first: 
        LOG.first: And enter the code:
        LOG.first: WDJB-MJHT
        LOG.first: 
        LOG.first: Once finished, enter the confirmation code provided in your browser.
        LOG.first: Confirmation code: Error: reading confirmation code: EOF
TestAccept/cmd/auth/login/device-code/non-interactive/errors: branch ✅ | main (aed6702) ❌ | latest (v1.23.0) ❌

main (aed6702):

acceptance_test.go:1341: Diff:
        --- cmd/auth/login/device-code/non-interactive/errors/output.txt
        +++ /tmp/TestAcceptcmdauthlogindevice-codenon-interactiveerrors740667354/002/output.txt
        @@ -5,94 +5,264 @@
         Error: --use-device-code is experimental. Set DATABRICKS_CLI_DEVICE_CODE=true to enable it
         
         >>> musterr [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Error: --use-device-code is experimental. Set DATABRICKS_CLI_DEVICE_CODE=true to enable it
        +Error: unknown flag: --user-code
         
        +Usage:
        +  databricks auth login [PROFILE] [flags]
        +
        +Flags:
        +      --client-id string       OAuth client ID to use for U2M authentication
        +      --configure-cluster      Prompts to configure cluster
        +      --configure-serverless   Prompts to configure serverless
        +  -h, --help                   help for login
        +      --resource stringArray   RFC 8707 resource indicator to scope the login to (repeatable). Requires --host.
        +      --scopes string          Comma-separated list of OAuth scopes to request (defaults to 'all-apis')
        +      --skip-workspace         Skip workspace selection for account-level access
        +      --timeout duration       Timeout for completing login challenge in the browser (default 1h0m0s)
        +
        +Global Flags:
        +      --account-id string     Databricks Account ID
        +      --debug                 enable debug logging
        +      --host string           Databricks Host
        +  -o, --output type           output type: text or json (default text)
        +  -p, --profile string        ~/.databrickscfg profile
        +  -t, --target string         bundle target to use (if applicable)
        +      --workspace-id string   Databricks Workspace ID
        +
        +
         === Flags the non-interactive login cannot honor
         
         >>> musterr [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --configure-cluster
        -Error: --configure-cluster is not supported by the non-interactive device code login. Set cluster_id on the profile after the login
        +Using a browser on another device, visit:
        +[DATABRICKS_URL]/device/verification
         
        +And enter the code:
        +WDJB-MJHT
        +
        +Once finished, enter the confirmation code provided in your browser.
        +Confirmation code: Error: reading confirmation code: EOF
        +
         === Missing host
         
         >>> musterr [CLI] auth login --use-device-code
        -Error: --use-device-code requires a workspace host. Pass it as an argument or with --host
        +Error: the command is being run in a non-interactive environment, please specify a host using --host
         
         >>> musterr [CLI] auth login --profile test --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Error: --use-device-code requires a workspace host. Pass it as an argument or with --host
        +Error: unknown flag: --user-code
         
        +Usage:
        +  databricks auth login [PROFILE] [flags]
        +
        +Flags:
        +      --client-id string       OAuth client ID to use for U2M authentication
        +      --configure-cluster      Prompts to configure cluster
        +      --configure-serverless   Prompts to configure serverless
        +  -h, --help                   help for login
        +      --resource stringArray   RFC 8707 resource indicator to scope the login to (repeatable). Requires --host.
        +      --scopes string          Comma-separated list of OAuth scopes to request (defaults to 'all-apis')
        +      --skip-workspace         Skip workspace selection for account-level access
        +      --timeout duration       Timeout for completing login challenge in the browser (default 1h0m0s)
        +
        +Global Flags:
        +      --account-id string     Databricks Account ID
        +      --debug                 enable debug logging
        +      --host string           Databricks Host
        +  -o, --output type           output type: text or json (default text)
        +  -p, --profile string        ~/.databrickscfg profile
        +  -t, --target string         bundle target to use (if applicable)
        +      --workspace-id string   Databricks Workspace ID
        +
        +
         === Incomplete flags of the second step
         
         >>> musterr [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --confirmation-code FTFA-QQKY
        -Error: --confirmation-code requires --user-code
        +Error: unknown flag: --confirmation-code
         
        +Usage:
        +  databricks auth login [PROFILE] [flags]
        +
        +Flags:
        +      --client-id string       OAuth client ID to use for U2M authentication
        +      --configure-cluster      Prompts to configure cluster
        +      --configure-serverless   Prompts to configure serverless
        +  -h, --help                   help for login
        +      --resource stringArray   RFC 8707 resource indicator to scope the login to (repeatable). Requires --host.
        +      --scopes string          Comma-separated list of OAuth scopes to request (defaults to 'all-apis')
        +      --skip-workspace         Skip workspace selection for account-level access
        +      --timeout duration       Timeout for completing login challenge in the browser (default 1h0m0s)
        +
        +Global Flags:
        +      --account-id string     Databricks Account ID
        +      --debug                 enable debug logging
        +      --host string           Databricks Host
        +  -o, --output type           output type: text or json (default text)
        +  -p, --profile string        ~/.databrickscfg profile
        +  -t, --target string         bundle target to use (if applicable)
        +      --workspace-id string   Databricks Workspace ID
        +
        +
         >>> musterr [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --user-code WDJB-MJHT
        -Error: --user-code requires --confirmation-code
        +Error: unknown flag: --user-code
         
        +Usage:
        +  databricks auth login [PROFILE] [flags]
        +
        +Flags:
        +      --client-id string       OAuth client ID to use for U2M authentication
        +      --configure-cluster      Prompts to configure cluster
        +      --configure-serverless   Prompts to configure serverless
        +  -h, --help                   help for login
        +      --resource stringArray   RFC 8707 resource indicator to scope the login to (repeatable). Requires --host.
        +      --scopes string          Comma-separated list of OAuth scopes to request (defaults to 'all-apis')
        +      --skip-workspace         Skip workspace selection for account-level access
        +      --timeout duration       Timeout for completing login challenge in the browser (default 1h0m0s)
        +
        +Global Flags:
        +      --account-id string     Databricks Account ID
        +      --debug                 enable debug logging
        +      --host string           Databricks Host
        +  -o, --output type           output type: text or json (default text)
        +  -p, --profile string        ~/.databrickscfg profile
        +  -t, --target string         bundle target to use (if applicable)
        +      --workspace-id string   Databricks Workspace ID
        +
        +
         >>> musterr [CLI] auth login --host [DATABRICKS_URL] --profile test --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Error: --confirmation-code requires --use-device-code
        +Error: unknown flag: --user-code
         
        +Usage:
        +  databricks auth login [PROFILE] [flags]
        +
        +Flags:
        +      --client-id string       OAuth client ID to use for U2M authentication
        +      --configure-cluster      Prompts to configure cluster
        +      --configure-serverless   Prompts to configure serverless
        +  -h, --help                   help for login
        +      --resource stringArray   RFC 8707 resource indicator to scope the login to (repeatable). Requires --host.
        +      --scopes string          Comma-separated list of OAuth scopes to request (defaults to 'all-apis')
        +      --skip-workspace         Skip workspace selection for account-level access
        +      --timeout duration       Timeout for completing login challenge in the browser (default 1h0m0s)
        +
        +Global Flags:
        +      --account-id string     Databricks Account ID
        +      --debug                 enable debug logging
        +      --host string           Databricks Host
        +  -o, --output type           output type: text or json (default text)
        +  -p, --profile string        ~/.databrickscfg profile
        +  -t, --target string         bundle target to use (if applicable)
        +      --workspace-id string   Databricks Workspace ID
        +
        +
         === Account host
         
         >>> musterr [CLI] auth login --host https://accounts.cloud.databricks.test --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Warn: [hostmetadata] failed to fetch host metadata for https://accounts.cloud.databricks.test, will skip for 1m0s
        -Error: --use-device-code is only supported for workspace hosts
        +Error: unknown flag: --user-code
         
        -=== Profile scoped to resources
        +Usage:
        +  databricks auth login [PROFILE] [flags]
         
        ->>> musterr [CLI] auth login --profile resources --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Error: --use-device-code cannot be used with profile resources because its login is scoped to resources. Log in without --use-device-code
        +Flags:
        +      --client-id string       OAuth client ID to use for U2M authentication
        +      --configure-cluster      Prompts to configure cluster
        +      --configure-serverless   Prompts to configure serverless
        +  -h, --help                   help for login
        +      --resource stringArray   RFC 8707 resource indicator to scope the login to (repeatable). Requires --host.
        +      --scopes string          Comma-separated list of OAuth scopes to request (defaults to 'all-apis')
        +      --skip-workspace         Skip workspace selection for account-level access
        +      --timeout duration       Timeout for completing login challenge in the browser (default 1h0m0s)
         
        -=== No pending login
        +Global Flags:
        +      --account-id string     Databricks Account ID
        +      --debug                 enable debug logging
        +      --host string           Databricks Host
        +  -o, --output type           output type: text or json (default text)
        +  -p, --profile string        ~/.databrickscfg profile
        +  -t, --target string         bundle target to use (if applicable)
        +      --workspace-id string   Databricks Workspace ID
         
        ->>> musterr [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Error: no pending device code login for profile test. Run the same command without --user-code and --confirmation-code to start a new login
         
        ->>> musterr [CLI] auth login --host [DATABRICKS_URL] --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Error: no pending device code login for host [DATABRICKS_URL]. Run the same command without --user-code and --confirmation-code to start a new login
... (126 more lines truncated, raise DATABRICKS_CLI_TEST_MAX_LOG to see more)

latest (v1.23.0):

acceptance_test.go:1341: Diff:
        --- cmd/auth/login/device-code/non-interactive/errors/output.txt
        +++ /tmp/TestAcceptcmdauthlogindevice-codenon-interactiveerrors2348250217/002/output.txt
        @@ -5,94 +5,264 @@
         Error: --use-device-code is experimental. Set DATABRICKS_CLI_DEVICE_CODE=true to enable it
         
         >>> musterr [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Error: --use-device-code is experimental. Set DATABRICKS_CLI_DEVICE_CODE=true to enable it
        +Error: unknown flag: --user-code
         
        +Usage:
        +  databricks auth login [PROFILE] [flags]
        +
        +Flags:
        +      --client-id string       OAuth client ID to use for U2M authentication
        +      --configure-cluster      Prompts to configure cluster
        +      --configure-serverless   Prompts to configure serverless
        +  -h, --help                   help for login
        +      --resource stringArray   RFC 8707 resource indicator to scope the login to (repeatable). Requires --host.
        +      --scopes string          Comma-separated list of OAuth scopes to request (defaults to 'all-apis')
        +      --skip-workspace         Skip workspace selection for account-level access
        +      --timeout duration       Timeout for completing login challenge in the browser (default 1h0m0s)
        +
        +Global Flags:
        +      --account-id string     Databricks Account ID
        +      --debug                 enable debug logging
        +      --host string           Databricks Host
        +  -o, --output type           output type: text or json (default text)
        +  -p, --profile string        ~/.databrickscfg profile
        +  -t, --target string         bundle target to use (if applicable)
        +      --workspace-id string   Databricks Workspace ID
        +
        +
         === Flags the non-interactive login cannot honor
         
         >>> musterr [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --configure-cluster
        -Error: --configure-cluster is not supported by the non-interactive device code login. Set cluster_id on the profile after the login
        +Using a browser on another device, visit:
        +[DATABRICKS_URL]/device/verification
         
        +And enter the code:
        +WDJB-MJHT
        +
        +Once finished, enter the confirmation code provided in your browser.
        +Confirmation code: Error: reading confirmation code: EOF
        +
         === Missing host
         
         >>> musterr [CLI] auth login --use-device-code
        -Error: --use-device-code requires a workspace host. Pass it as an argument or with --host
        +Error: the command is being run in a non-interactive environment, please specify a host using --host
         
         >>> musterr [CLI] auth login --profile test --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Error: --use-device-code requires a workspace host. Pass it as an argument or with --host
        +Error: unknown flag: --user-code
         
        +Usage:
        +  databricks auth login [PROFILE] [flags]
        +
        +Flags:
        +      --client-id string       OAuth client ID to use for U2M authentication
        +      --configure-cluster      Prompts to configure cluster
        +      --configure-serverless   Prompts to configure serverless
        +  -h, --help                   help for login
        +      --resource stringArray   RFC 8707 resource indicator to scope the login to (repeatable). Requires --host.
        +      --scopes string          Comma-separated list of OAuth scopes to request (defaults to 'all-apis')
        +      --skip-workspace         Skip workspace selection for account-level access
        +      --timeout duration       Timeout for completing login challenge in the browser (default 1h0m0s)
        +
        +Global Flags:
        +      --account-id string     Databricks Account ID
        +      --debug                 enable debug logging
        +      --host string           Databricks Host
        +  -o, --output type           output type: text or json (default text)
        +  -p, --profile string        ~/.databrickscfg profile
        +  -t, --target string         bundle target to use (if applicable)
        +      --workspace-id string   Databricks Workspace ID
        +
        +
         === Incomplete flags of the second step
         
         >>> musterr [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --confirmation-code FTFA-QQKY
        -Error: --confirmation-code requires --user-code
        +Error: unknown flag: --confirmation-code
         
        +Usage:
        +  databricks auth login [PROFILE] [flags]
        +
        +Flags:
        +      --client-id string       OAuth client ID to use for U2M authentication
        +      --configure-cluster      Prompts to configure cluster
        +      --configure-serverless   Prompts to configure serverless
        +  -h, --help                   help for login
        +      --resource stringArray   RFC 8707 resource indicator to scope the login to (repeatable). Requires --host.
        +      --scopes string          Comma-separated list of OAuth scopes to request (defaults to 'all-apis')
        +      --skip-workspace         Skip workspace selection for account-level access
        +      --timeout duration       Timeout for completing login challenge in the browser (default 1h0m0s)
        +
        +Global Flags:
        +      --account-id string     Databricks Account ID
        +      --debug                 enable debug logging
        +      --host string           Databricks Host
        +  -o, --output type           output type: text or json (default text)
        +  -p, --profile string        ~/.databrickscfg profile
        +  -t, --target string         bundle target to use (if applicable)
        +      --workspace-id string   Databricks Workspace ID
        +
        +
         >>> musterr [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --user-code WDJB-MJHT
        -Error: --user-code requires --confirmation-code
        +Error: unknown flag: --user-code
         
        +Usage:
        +  databricks auth login [PROFILE] [flags]
        +
        +Flags:
        +      --client-id string       OAuth client ID to use for U2M authentication
        +      --configure-cluster      Prompts to configure cluster
        +      --configure-serverless   Prompts to configure serverless
        +  -h, --help                   help for login
        +      --resource stringArray   RFC 8707 resource indicator to scope the login to (repeatable). Requires --host.
        +      --scopes string          Comma-separated list of OAuth scopes to request (defaults to 'all-apis')
        +      --skip-workspace         Skip workspace selection for account-level access
        +      --timeout duration       Timeout for completing login challenge in the browser (default 1h0m0s)
        +
        +Global Flags:
        +      --account-id string     Databricks Account ID
        +      --debug                 enable debug logging
        +      --host string           Databricks Host
        +  -o, --output type           output type: text or json (default text)
        +  -p, --profile string        ~/.databrickscfg profile
        +  -t, --target string         bundle target to use (if applicable)
        +      --workspace-id string   Databricks Workspace ID
        +
        +
         >>> musterr [CLI] auth login --host [DATABRICKS_URL] --profile test --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Error: --confirmation-code requires --use-device-code
        +Error: unknown flag: --user-code
         
        +Usage:
        +  databricks auth login [PROFILE] [flags]
        +
        +Flags:
        +      --client-id string       OAuth client ID to use for U2M authentication
        +      --configure-cluster      Prompts to configure cluster
        +      --configure-serverless   Prompts to configure serverless
        +  -h, --help                   help for login
        +      --resource stringArray   RFC 8707 resource indicator to scope the login to (repeatable). Requires --host.
        +      --scopes string          Comma-separated list of OAuth scopes to request (defaults to 'all-apis')
        +      --skip-workspace         Skip workspace selection for account-level access
        +      --timeout duration       Timeout for completing login challenge in the browser (default 1h0m0s)
        +
        +Global Flags:
        +      --account-id string     Databricks Account ID
        +      --debug                 enable debug logging
        +      --host string           Databricks Host
        +  -o, --output type           output type: text or json (default text)
        +  -p, --profile string        ~/.databrickscfg profile
        +  -t, --target string         bundle target to use (if applicable)
        +      --workspace-id string   Databricks Workspace ID
        +
        +
         === Account host
         
         >>> musterr [CLI] auth login --host https://accounts.cloud.databricks.test --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Warn: [hostmetadata] failed to fetch host metadata for https://accounts.cloud.databricks.test, will skip for 1m0s
        -Error: --use-device-code is only supported for workspace hosts
        +Error: unknown flag: --user-code
         
        -=== Profile scoped to resources
        +Usage:
        +  databricks auth login [PROFILE] [flags]
         
        ->>> musterr [CLI] auth login --profile resources --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Error: --use-device-code cannot be used with profile resources because its login is scoped to resources. Log in without --use-device-code
        +Flags:
        +      --client-id string       OAuth client ID to use for U2M authentication
        +      --configure-cluster      Prompts to configure cluster
        +      --configure-serverless   Prompts to configure serverless
        +  -h, --help                   help for login
        +      --resource stringArray   RFC 8707 resource indicator to scope the login to (repeatable). Requires --host.
        +      --scopes string          Comma-separated list of OAuth scopes to request (defaults to 'all-apis')
        +      --skip-workspace         Skip workspace selection for account-level access
        +      --timeout duration       Timeout for completing login challenge in the browser (default 1h0m0s)
         
        -=== No pending login
        +Global Flags:
        +      --account-id string     Databricks Account ID
        +      --debug                 enable debug logging
        +      --host string           Databricks Host
        +  -o, --output type           output type: text or json (default text)
        +  -p, --profile string        ~/.databrickscfg profile
        +  -t, --target string         bundle target to use (if applicable)
        +      --workspace-id string   Databricks Workspace ID
         
        ->>> musterr [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Error: no pending device code login for profile test. Run the same command without --user-code and --confirmation-code to start a new login
         
        ->>> musterr [CLI] auth login --host [DATABRICKS_URL] --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Error: no pending device code login for host [DATABRICKS_URL]. Run the same command without --user-code and --confirmation-code to start a new login
... (126 more lines truncated, raise DATABRICKS_CLI_TEST_MAX_LOG to see more)
TestAccept/cmd/auth/login/device-code/non-interactive/flags: branch ✅ | main (aed6702) ❌ | latest (v1.23.0) ❌

main (aed6702):

acceptance_test.go:1341: Diff:
        --- cmd/auth/login/device-code/non-interactive/flags/output.txt
        +++ /tmp/TestAcceptcmdauthlogindevice-codenon-interactiveflags2252594984/002/output.txt
        @@ -8,29 +8,7 @@
         And enter the code:
         WDJB-MJHT
         
        -Once finished, complete the login by running the same command with
        ---user-code WDJB-MJHT and --confirmation-code set to the confirmation code
        -provided in your browser.
        +Once finished, enter the confirmation code provided in your browser.
        +Confirmation code: Error: reading confirmation code: EOF
         
        ->>> [CLI] auth login --profile scoped --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Profile scoped was successfully saved
        -
        ->>> print_requests.py --oneline //oidc/v1/device/auth //oidc/v1/token
        -{"method": "POST", "path": "/oidc/v1/device/auth", "raw_body": "client_id=databricks-cli&scope=offline_access+sql"}
        -{"method": "POST", "path": "/oidc/v1/token", "raw_body": "client_id=databricks-cli&confirmation_code=FTFAQQKY&device_code=device-code-1&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code"}
        -
        -=== Serverless compute is configured in the second step
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile serverless --use-device-code --configure-serverless
        -Using a browser on another device, visit:
        -[DATABRICKS_URL]/device/verification
        -
        -And enter the code:
        -WDJB-MJHT
        -
        -Once finished, complete the login by running the same command with
        ---user-code WDJB-MJHT and --confirmation-code set to the confirmation code
        -provided in your browser.
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile serverless --use-device-code --configure-serverless --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Profile serverless was successfully saved
        +Exit code: 1
        
    acceptance_test.go:1312: Missing output file: out.databrickscfg

latest (v1.23.0): same output as main (aed6702)

TestAccept/cmd/auth/login/device-code/non-interactive/nominal: branch ✅ | main (aed6702) ❌ | latest (v1.23.0) ❌

main (aed6702):

acceptance_test.go:1341: Diff:
        --- cmd/auth/login/device-code/non-interactive/nominal/output.txt
        +++ /tmp/TestAcceptcmdauthlogindevice-codenon-interactivenominal1865103611/002/output.txt
        @@ -8,106 +8,7 @@
         And enter the code:
         WDJB-MJHT
         
        -Once finished, complete the login by running the same command with
        ---user-code WDJB-MJHT and --confirmation-code set to the confirmation code
        -provided in your browser.
        +Once finished, enter the confirmation code provided in your browser.
        +Confirmation code: Error: reading confirmation code: EOF
         
        -=== The first step stores the device code and saves no profile
        -
        ->>> find.py home
        -home/.databricks/token-cache.json
        -
        ->>> cache_keys
        -[
        -  "[DATABRICKS_URL]#device-code"
        -]
        -
        -=== Confirm the login
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -
        ->>> [CLI] auth token --host [DATABRICKS_URL]
        -json.access_token = "oauth-token";
        -
        ->>> cache_keys
        -[
        -  "[DATABRICKS_URL]"
        -]
        -
        -=== Start the login with a profile
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code
        -Using a browser on another device, visit:
        -[DATABRICKS_URL]/device/verification
        -
        -And enter the code:
        -WDJB-MJHT
        -
        -Once finished, complete the login by running the same command with
        ---user-code WDJB-MJHT and --confirmation-code set to the confirmation code
        -provided in your browser.
        -
        -=== Confirm the login
        -
        ->>> [CLI] auth login --host [DATABRICKS_URL] --profile test --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Profile test was successfully saved
        -
        ->>> [CLI] auth token --profile test
        -json.access_token = "oauth-token";
        -
        ->>> [CLI] auth profiles
        -Name            Host                    Valid
        -test (Default)  [DATABRICKS_URL]  YES
        -
        -=== Start the login again with the host from the saved profile
        -
        ->>> [CLI] auth login --profile test --use-device-code
        -Using a browser on another device, visit:
        -[DATABRICKS_URL]/device/verification
        -
        -And enter the code:
        -WDJB-MJHT
        -
        -Once finished, complete the login by running the same command with
        ---user-code WDJB-MJHT and --confirmation-code set to the confirmation code
        -provided in your browser.
        -
        ->>> [CLI] auth token --profile test
        -json.access_token = "oauth-token";
        -
        ->>> [CLI] auth login --profile test --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Profile test was successfully saved
        -
        ->>> cache_keys
        -[
        -  "[DATABRICKS_URL]",
        -  "test"
        -]
        -
        ->>> print_requests.py --oneline //oidc/v1/device/auth //oidc/v1/token
        -{"method": "POST", "path": "/oidc/v1/device/auth", "raw_body": "client_id=databricks-cli&scope=offline_access+all-apis"}
        -{"method": "POST", "path": "/oidc/v1/token", "raw_body": "client_id=databricks-cli&confirmation_code=FTFAQQKY&device_code=device-code-1&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code"}
        -{"method": "POST", "path": "/oidc/v1/device/auth", "raw_body": "client_id=databricks-cli&scope=offline_access+all-apis"}
        -{"method": "POST", "path": "/oidc/v1/token", "raw_body": "client_id=databricks-cli&confirmation_code=FTFAQQKY&device_code=device-code-2&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code"}
        -{"method": "POST", "path": "/oidc/v1/device/auth", "raw_body": "client_id=databricks-cli&scope=offline_access+all-apis"}
        -{"method": "POST", "path": "/oidc/v1/token", "raw_body": "client_id=databricks-cli&confirmation_code=FTFAQQKY&device_code=device-code-3&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code"}
        -
        -=== An AI agent gets notes on passing the login on to the user
        -
        ->>> AGENT=test-agent [CLI] auth login --host [DATABRICKS_URL] --profile agent --use-device-code
        -Using a browser on another device, visit:
        -[DATABRICKS_URL]/device/verification
        -
        -And enter the code:
        -WDJB-MJHT
        -
        -Note for AI agents (test-agent): give the URL and the code above to the user.
        -Do not open the URL or approve the login yourself: the user must approve it.
        -Ask the user for the confirmation code their browser shows.
        -
        -Once finished, complete the login by running the same command with
        ---user-code WDJB-MJHT and --confirmation-code set to the confirmation code
        -provided in your browser.
        -
        ->>> AGENT=test-agent [CLI] auth login --host [DATABRICKS_URL] --profile agent --use-device-code --user-code WDJB-MJHT --confirmation-code FTFA-QQKY
        -Profile agent was successfully saved
        +Exit code: 1
        
    acceptance_test.go:1312: Missing output file: out.databrickscfg

latest (v1.23.0): same output as main (aed6702)

TestAccept/cmd/auth/login/device-code/unsupported-host: branch ✅ | main (aed6702) ❌ | latest (v1.23.0) ❌

main (aed6702):

acceptance_test.go:1341: Diff:
        --- cmd/auth/login/device-code/unsupported-host/output.txt
        +++ /tmp/TestAcceptcmdauthlogindevice-codeunsupported-host1673316066/002/output.txt
        @@ -2,7 +2,7 @@
         === No host
         
         >>> musterr [CLI] auth login --profile new-profile --use-device-code
        -Error: --use-device-code requires a workspace host. Pass it as an argument or with --host
        +Error: the command is being run in a non-interactive environment, please specify a host using --host
         
         === Account host

latest (v1.23.0): same output as main (aed6702)

TestAccept/cmd/auth/logout/pending-device-code-login: branch ✅ | main (aed6702) ❌ | latest (v1.23.0) ❌

main (aed6702):

acceptance_test.go:1341: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1341
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1082
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:641
        	Error:      	Not equal: 
        	            	expected: "\n=== Logout and delete dev\n>>> [CLI] auth logout --profile dev --auto-approve --delete\nLogged out of and deleted profile \"dev\".\n\n=== Token cache keys after logout — the host keys are kept, staging shares them\n[\n  \"[DATABRICKS_URL]\",\n  \"[DATABRICKS_URL]#device-code\",\n  \"staging\",\n  \"staging#device-code\"\n]\n\n=== Logout staging\n>>> [CLI] auth logout --profile staging --auto-approve\nLogged out of profile \"staging\". Use --delete to also remove it from the config file.\n\n=== Token cache keys after logout — no profile uses the host anymore\n[]\n"
        	            	actual  : "\n=== Logout and delete dev\n>>> [CLI] auth logout --profile dev --auto-approve --delete\nLogged out of and deleted profile \"dev\".\n\n=== Token cache keys after logout — the host keys are kept, staging shares them\n[\n  \"dev#device-code\",\n  \"[DATABRICKS_URL]\",\n  \"[DATABRICKS_URL]#device-code\",\n  \"staging\",\n  \"staging#device-code\"\n]\n\n=== Logout staging\n>>> [CLI] auth logout --profile staging --auto-approve\nLogged out of profile \"staging\". Use --delete to also remove it from the config file.\n\n=== Token cache keys after logout — no profile uses the host anymore\n[\n  \"dev#device-code\",\n  \"[DATABRICKS_URL]#device-code\",\n  \"staging#device-code\"\n]\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -7,2 +7,3 @@
        	            	 [
        	            	+  "dev#device-code",
        	            	   "[DATABRICKS_URL]",
        	            	@@ -18,3 +19,7 @@
        	            	 === Token cache keys after logout — no profile uses the host anymore
        	            	-[]
        	            	+[
        	            	+  "dev#device-code",
        	            	+  "[DATABRICKS_URL]#device-code",
        	            	+  "staging#device-code"
        	            	+]
        	            	 
        	Test:       	TestAccept/cmd/auth/logout/pending-device-code-login
        	Messages:   	cmd/auth/logout/pending-device-code-login/output.txt vs /tmp/TestAcceptcmdauthlogoutpending-device-code-login800539412/002/output.txt

latest (v1.23.0): same output as main (aed6702)

@maksim-zuev-db
maksim-zuev-db removed this pull request from stack #7236 October 28, 2026 15:57
@maksim-zuev-db
maksim-zuev-db marked this pull request as ready for review October 28, 2026 16:02
@maksim-zuev-db
maksim-zuev-db requested review from a team as code owners October 28, 2026 16:02
@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: f52b31d

Run: 40914310997

Env ✅​pass 🙈​skip Time
✅​ aws linux-2core-8gb 281 21 5:23
✅​ aws-windows-latest-4core-16gb 283 19 3:44
✅​ azure linux-2core-8gb 280 21 5:21
✅​ azure-windows-latest-4core-16gb 282 19 4:04
✅​ gcp linux-2core-8gb 281 21 5:31
✅​ gcp-windows-latest-4core-16gb 283 19 3:43
Top 6 slowest tests (at least 2 minutes):
duration env testname
3:59 azure-windows-latest-4core-16gb TestAccept
3:55 azure linux-2core-8gb TestAccept
3:40 aws-windows-latest-4core-16gb TestAccept
3:38 gcp linux-2core-8gb TestAccept
3:36 gcp-windows-latest-4core-16gb TestAccept
3:31 aws linux-2core-8gb TestAccept

maksim-zuev-db and others added 5 commits October 29, 2026 16:13
When an AI agent is detected, the first step of the non-interactive
login also prints a note that tells it to give the URL and the user code
to the user, not to approve the login itself, and to ask the user for
the confirmation code.

Co-authored-by: Isaac <no-reply@databricks.com>
The device code login built the "Note for AI agents (<name>): " prefix
by hand, like AgentNotice does. agent.Note now adds it for both, and
returns an empty string when no agent is detected. AgentNotice prints
the same text as before.

Co-authored-by: Isaac <no-reply@databricks.com>
An agent that runs the login in a terminal, e.g. through a pseudo-terminal,
got the interactive prompt without the note, so nothing told it to pass
the login on to the user. The note is now part of the approval
instructions that both logins print.

The prompt tests clear the agent environment, so that running them from
an agent does not change the output.

Co-authored-by: Isaac <no-reply@databricks.com>
The interactive device code login printed the note of the non-interactive
one, which tells the agent to ask the user for the confirmation code but
not what to do with it. That login waits for the code at its prompt, so
its note now tells the agent to enter the code there.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
@maksim-zuev-db
maksim-zuev-db force-pushed the device-code-agent-notes branch from 03acd36 to 46f6bad Compare October 29, 2026 16:29
Each note is used once, so it moves next to its use.

Co-authored-by: Isaac <no-reply@databricks.com>

@simonfaltum simonfaltum left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, the note reads well and AgentNotice prints the same text as before. The main thing is the terminal-flow note, which I don't think will work for agents (inline). The rest are small.

Comment on lines +277 to +278
agentNote := deviceCodeAgentNote + "Ask the user for the confirmation code their browser shows, and enter it\n" +
`at the "Confirmation code" prompt of this command.`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

An agent won't have an interactive terminal, so I don't think this note will work for it. Without a terminal it already gets the two-step flow and never sees this text. If the command does run in a PTY (some IDE agent terminals), the agent usually can't type into a running command, so it waits at the prompt until the login times out.

Could agent detection pick the two-step flow (start, then rerun with --user-code and --confirmation-code) even when stdin is a terminal? Then this variant of the note can go, along with the README line that says agent detection doesn't pick the flow. (If the note stays, "Confirmation code" here duplicates the AskCancellable label below, and .agents/rules/style-guide-go.md asks for a shared constant.)

return fmt.Errorf("store pending login: %w", err)
}
cmdio.LogString(ctx, strings.Join(append(approvalInstructions(*da),
agentNote := deviceCodeAgentNote + "Ask the user for the confirmation code their browser shows."

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[nice to have] In the two-step flow the agent runs step two, so it's also the one that reads the wrong-code error in complete() (L244): "Check the confirmation code and approve the login if you haven't, then rerun this command." Read literally, that tells the agent to approve the login, which this note says it must not do. It shows up whenever the user passes on a wrong code or hasn't approved yet. Could you add an agent note there, e.g. agent.AgentNote("ask the user to approve the login and to check the confirmation code. Do not approve it yourself."), or reword it to "make sure the login was approved in the browser"? The text comes from #7220, so fixing it there works too.

// Note: use of this function is incompatible with parallel execution.
func SetAgent(t TestingT, agent string) {
NullEnvironment(t)
if agent != "" {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[nit] SetAgent(t, "") only clears the environment through NullEnvironment and never calls t.Setenv, so a test that also calls t.Parallel() won't panic, even though the comment says parallel use isn't supported. Calling t.Setenv("AGENT", agent) unconditionally behaves the same (with nothing else set, the SDK treats an empty AGENT as no agent) and adds the guard.

trace print_requests.py --oneline //oidc/v1/device/auth //oidc/v1/token

title "An AI agent gets notes on passing the login on to the user\n"
trace AGENT=test-agent $CLI auth login --host $DATABRICKS_HOST --profile agent --use-device-code

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[nit] This is the first acceptance output that depends on agent detection, and the runner's unset list in acceptance/acceptance_test.go is missing PI_CODING_AGENT, which SDK v0.191.0 detects. When the suite runs inside Pi, the note says (pi) instead of (test-agent) and this test fails. Adding PI_CODING_AGENT to that list fixes it. libs/aitools/agents/hint_test.go has the same gap and could switch to testutil.SetAgent in a follow-up.

This branch has not been deployed

No deployments
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.

3 participants