Skip to content

feat: add --use-device-code flag to auth login#256

Closed
iemejia wants to merge 1 commit into
microsoft:mainfrom
iemejia:login-device-code
Closed

feat: add --use-device-code flag to auth login#256
iemejia wants to merge 1 commit into
microsoft:mainfrom
iemejia:login-device-code

Conversation

@iemejia

@iemejia iemejia commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Add device code authentication flow for `fab auth login`, similar to `az login --use-device-code` in Azure CLI. This allows users to authenticate from environments where browser access is not available (SSH sessions, containers, remote servers, CI runners).

Usage

# Command-line
fab auth login --use-device-code
fab auth login --use-device-code --tenant <tenant_id>

# Interactive menu: \"Device code\" is now an option
fab auth login

The CLI displays a message like:

To sign in, use a web browser to open https://microsoft.com/devicelogin and enter the code XXXXXXXX

The user completes authentication on any device with a browser, and the CLI picks up the token.

Prerequisites to enable this feature

The current Fabric CLI app registration (`5814bfb4-2705-4994-b8d6-39aabeb5eaeb`) is configured as a confidential client. Device code flow is a public client flow (no client credentials are sent), so Azure AD currently rejects it with:

AADSTS7000218: The request body must contain the following parameter:
'client_assertion' or 'client_secret'.

To enable this feature, the app registration needs "Allow public client flows" enabled:

  • Azure Portal: App registrations > Authentication > Advanced settings > Allow public client flows > Yes
  • Manifest: Set `"allowPublicClient": true`

Once that setting is enabled, device code flow will work end-to-end.

Implementation details

  • Added `--use-device-code` argument to the auth login parser
  • Added `_acquire_token_by_device_code()` method using MSAL's `initiate_device_flow` and `acquire_token_by_device_flow` APIs
  • Silent refresh with `force_refresh=True` is attempted for subsequent scopes after the initial device code auth, so the user only needs to enter the code once
  • `_use_device_code` flag is wrapped in `try/finally` to ensure cleanup on failure
  • Fixed `acquire_token_silent` error handling: MSAL can return `{"error": "..."}` (not just `None`) when silent acquisition fails — now correctly falls through to interactive/device code flow
  • Added `device_code_flow_failed()` error message

Tests

@iemejia iemejia requested a review from a team as a code owner June 30, 2026 20:32
Copilot AI review requested due to automatic review settings June 30, 2026 20:32
@iemejia iemejia force-pushed the login-device-code branch from 3f00d3a to 49775d9 Compare June 30, 2026 20:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds an optional device-code authentication path to fab auth login so users can authenticate in environments without browser access (e.g., SSH/containers/CI), integrating MSAL’s device flow into the existing FabAuth token acquisition pipeline.

Changes:

  • Introduces --use-device-code to the auth login parser and adds an interactive menu option (“Device code”).
  • Implements a device-code token acquisition helper in FabAuth and wires it into acquire_token() as an alternative to interactive browser auth.
  • Adds/updates command-level and core-level tests covering device-code success, silent-refresh behavior, fallbacks, and error cases.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/fabric_cli/core/fab_auth.py Adds device-code flow helper and routes user auth renewals through device-code when enabled; adjusts silent-failure handling to treat MSAL { "error": ... } as a failure.
src/fabric_cli/commands/auth/fab_auth.py Exposes device-code login via CLI flag and interactive selection; toggles device-code mode during token acquisition.
src/fabric_cli/parsers/fab_auth_parser.py Adds --use-device-code flag and help example to fab auth login.
src/fabric_cli/errors/auth.py Adds a dedicated error message for device-code flow initiation failures.
tests/test_core/test_fab_auth.py Adds unit tests for device-code helper and acquire-token integration behavior.
tests/test_commands/test_auth.py Adds command-level tests for CLI flag behavior, interactive selection, precedence rules, and flag cleanup.

Comment thread src/fabric_cli/core/fab_auth.py Outdated
Comment on lines +31 to +36
if args.use_device_code:
FabAuth().set_access_mode("user", args.tenant)
FabAuth()._use_device_code = True
try:
FabAuth().get_access_token(scope=fab_constant.SCOPE_FABRIC_DEFAULT)
FabAuth().get_access_token(scope=fab_constant.SCOPE_ONELAKE_DEFAULT)
Comment on lines +88 to +96
elif selected_auth == "Device code":
FabAuth().set_access_mode("user", args.tenant)
FabAuth()._use_device_code = True
try:
FabAuth().get_access_token(scope=fab_constant.SCOPE_FABRIC_DEFAULT)
FabAuth().get_access_token(scope=fab_constant.SCOPE_ONELAKE_DEFAULT)
FabAuth().get_access_token(scope=fab_constant.SCOPE_AZURE_DEFAULT)
finally:
FabAuth()._use_device_code = False
Copilot AI review requested due to automatic review settings June 30, 2026 20:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Comment thread tests/test_core/test_fab_auth.py Outdated
Comment on lines 877 to 879
assert exc_info.value.status_code == con.ERROR_AUTHENTICATION_FAILED
assert (
exc_info.value.message
== ErrorMessages.Auth.token_acquisition_failed()
)
assert exc_info.value.message == ErrorMessages.Auth.token_acquisition_failed()
assert exc_info.value.status_code == con.ERROR_AUTHENTICATION_FAILED
Comment thread src/fabric_cli/core/fab_auth.py Outdated
)
return self.app

def _acquire_token_by_device_code(self, scope: list[str]) -> dict:
Comment on lines +31 to +35
if args.use_device_code:
FabAuth().set_access_mode("user", args.tenant)
FabAuth()._use_device_code = True
try:
FabAuth().get_access_token(scope=fab_constant.SCOPE_FABRIC_DEFAULT)
Comment on lines +88 to +92
elif selected_auth == "Device code":
FabAuth().set_access_mode("user", args.tenant)
FabAuth()._use_device_code = True
try:
FabAuth().get_access_token(scope=fab_constant.SCOPE_FABRIC_DEFAULT)
Copilot AI review requested due to automatic review settings June 30, 2026 20:39
@iemejia iemejia force-pushed the login-device-code branch from 75d57ba to 5d6a1d2 Compare June 30, 2026 20:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

src/fabric_cli/core/fab_auth.py:274

  • _acquire_token_by_device_code is currently defined at module scope (no class indentation). This makes self._acquire_token_by_device_code(...) in acquire_token() fail and will also trigger an IndentationError when the next method definition is encountered. Indent the def line so it becomes a proper FabAuth method.
                        con.IDENTITY_TYPE: "service_principal",

Copilot AI review requested due to automatic review settings June 30, 2026 20:42
Add device code authentication flow for environments where browser
access is not available. Users can now authenticate by entering a code
at https://microsoft.com/devicelogin from any device with a browser.

Usage:
  fab auth login --use-device-code
  fab auth login --use-device-code --tenant <tenant_id>

The option is also available in the interactive authentication menu
as "Device code".

Implementation:
- Add --use-device-code argument to the auth login parser
- Add _acquire_token_by_device_code() using MSAL's initiate_device_flow
  and acquire_token_by_device_flow APIs
- Add device_code_flow_failed() error message
- Wrap device code flow in try/finally to ensure flag cleanup on failure
- Use silent refresh for subsequent scopes after initial device code auth

Assisted-by: GitHub Copilot:claude-opus-4.6
@iemejia iemejia force-pushed the login-device-code branch from 5d6a1d2 to 7aa9a30 Compare June 30, 2026 20:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment thread tests/test_core/test_fab_auth.py Outdated
Comment on lines 877 to 879
assert exc_info.value.status_code == con.ERROR_AUTHENTICATION_FAILED
assert (
exc_info.value.message
== ErrorMessages.Auth.token_acquisition_failed()
)
assert exc_info.value.message == ErrorMessages.Auth.token_acquisition_failed()
assert exc_info.value.status_code == con.ERROR_AUTHENTICATION_FAILED
Comment thread src/fabric_cli/core/fab_auth.py Outdated
Comment on lines +309 to +311
Raises:
FabricCLIError: When device code flow initiation or token acquisition fails
"""
Copilot AI review requested due to automatic review settings June 30, 2026 20:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Comment on lines +72 to +76
self._use_device_code = True
try:
yield
finally:
self._use_device_code = False
prompt="select_account",
parent_window_handle=msal.PublicClientApplication.CONSOLE_WINDOW_HANDLE,
)
if (token is None or token.get("error")) and interactive_renew:
Comment on lines +306 to +310
Returns:
Full MSAL result dictionary containing access_token, expires_on, etc.,
or None if the device flow returns no result. Raises:
FabricCLIError: When device code flow initiation or token acquisition fails
"""
@iemejia iemejia closed this Jun 30, 2026
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.

2 participants