feat: add --use-device-code flag to auth login#256
Closed
iemejia wants to merge 1 commit into
Closed
Conversation
3f00d3a to
49775d9
Compare
Contributor
There was a problem hiding this comment.
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-codeto the auth login parser and adds an interactive menu option (“Device code”). - Implements a device-code token acquisition helper in
FabAuthand wires it intoacquire_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 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 |
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 |
| ) | ||
| 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) |
75d57ba to
5d6a1d2
Compare
Contributor
There was a problem hiding this comment.
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_codeis currently defined at module scope (no class indentation). This makesself._acquire_token_by_device_code(...)inacquire_token()fail and will also trigger anIndentationErrorwhen the next method definition is encountered. Indent thedefline so it becomes a properFabAuthmethod.
con.IDENTITY_TYPE: "service_principal",
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
5d6a1d2 to
7aa9a30
Compare
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 on lines
+309
to
+311
| Raises: | ||
| FabricCLIError: When device code flow initiation or token acquisition fails | ||
| """ |
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 | ||
| """ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
The CLI displays a message like:
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:
To enable this feature, the app registration needs "Allow public client flows" enabled:
Once that setting is enabled, device code flow will work end-to-end.
Implementation details
Tests
2>&1
GraphQL: Resource protected by organization SAML enforcement. You must grant your OAuth token access to this organization. (repository)
Authorize in your web browser: https://github.com/enterprises/microsoftopensource/sso?authorization_request=AAATM5BKJANCNTQNU5PG2YTKIQ2NBA5PN5ZGOYLONF5GC5DJN5XF62LEZYAF32PCVVRXEZLEMVXHI2LBNRPWSZGPAAAAAAI4P3ULNL3DOJSWIZLOORUWC3C7OR4XAZNLJ5QXK5DIIFRWGZLTOM
Still blocked. The gh token hasn't been SSO-authorized yet. I literally cannot create the PR via the API until that authorization goes through.
Go to this URL to create it from the browser (it will pre-fill from your branch):
https://github.com/microsoft/fabric-cli/compare/main...iemejia:fabric-cli:login-device-code?expand=1
That's the PR creation page. Your browser session should already have SSO access. Just paste the title/body I gave you in the previous message.