Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
24582d3
feat: Add Azure CLI auth source (POC)
Jul 15, 2026
98e325b
Merge branch 'main' into feature/azure-cli-auth-poc
shirasassoon Aug 11, 2026
c0ac959
feat: production-ready Azure CLI auth hardening
Aug 11, 2026
8d48a1a
perf: validate only Fabric scope at Azure CLI login
Aug 11, 2026
aef875a
fix: restore 3-scope validation for Azure CLI login consistency
Aug 11, 2026
a2c5de6
fix: add tenant display to interactive Azure CLI login path
Aug 11, 2026
7c499a2
update
Aug 11, 2026
a0215d2
perf: cache az account show result for 30s to avoid repeated subproce…
Aug 11, 2026
250e8fb
fix: address tenant cache review feedback
Aug 11, 2026
3aed687
Merge branch 'main' into feature/azure-cli-auth-poc
shirasassoon Aug 11, 2026
28bf323
refactor: address code review feedback on Azure CLI auth
Aug 11, 2026
277dd0d
refactor: move Azure CLI error messages to ErrorMessages.Auth
Aug 12, 2026
8ed9892
fix wording
Aug 12, 2026
39b7de1
Merge branch 'feature/azure-cli-auth-poc' of https://github.com/shira…
Aug 12, 2026
cb334eb
refactor: replace denylist sanitization with SDK exception allowlist
Aug 12, 2026
da260d3
refactor: move azure-identity import to module level
Aug 12, 2026
7705598
fix comment
Aug 12, 2026
5be8a2a
chore: remove unused fresh_auth fixture
Aug 12, 2026
22ee7d9
fix: add azure_cli to test args fixture to prevent MagicMock truthy leak
Aug 12, 2026
40213f0
add changelog
Aug 12, 2026
b731895
Merge branch 'feature/azure-cli-auth-poc' of https://github.com/shira…
Aug 12, 2026
3e09380
test: expand Azure CLI auth test coverage
Aug 12, 2026
3c7fd91
test: address review feedback on test quality
Aug 12, 2026
e91c94b
test: add parser and MSAL bridge coverage for Azure CLI auth
Aug 12, 2026
76917c6
test: address review feedback on test quality (round 2)
Aug 12, 2026
af7b21a
test: address review feedback round 3
Aug 12, 2026
cbeba01
test: use monkeypatch.setattr for singleton file paths
Aug 12, 2026
f560488
refactor: extract token refresh buffer to named constant
Aug 12, 2026
02f7618
fix: clear token cache on set_azure_cli to prevent stale cross-tenant…
Aug 12, 2026
8a3ae91
fix: resolve az executable path for Windows compatibility
Aug 12, 2026
a4d4ec4
format fix
Aug 12, 2026
7ec4ee1
docs: add Azure CLI authentication to command reference and examples
Aug 12, 2026
2a6ffe1
docs: add Azure CLI authentication to command reference and examples
Aug 12, 2026
6624a1a
v
Aug 12, 2026
8fae5dc
update docs
Aug 12, 2026
4851ffa
update wording
Aug 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/added-20260812-122446.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: added
body: Add support for Azure CLI authentication source
time: 2026-08-12T12:24:46.8603823+03:00
custom:
Author: shirasassoon
AuthorLink: https://github.com/shirasassoon
37 changes: 29 additions & 8 deletions docs/commands/auth/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ Not resource-specific; applies to CLI authentication context.

## Available Commands

| Command | Description | Usage |
|----------------|---------------------------|-----------------------------------------------------------------------|
| `auth login` | Log in to Fabric CLI | `auth login [parameters]` |
| `auth logout` | Log out of current session| `auth logout` |
| `auth status` | Show authentication status| `auth status` |
| Command | Description | Usage |
| --- | --- | --- |
| `auth login` | Log in to Fabric CLI | `auth login [parameters]` |
| `auth logout` | Log out of current session | `auth logout` |
| `auth status` | Show authentication status | `auth status` |

---

Expand All @@ -22,8 +22,28 @@ Authenticate with Fabric CLI.

**Usage:**

#### Interactive login
```
fab auth login [-u <client_id>] [-p <client_secret>] [--federated-token <token>] [--certificate </path/to/certificate.[pem|p12|pfx]>] [--tenant <tenant_id>]
fab auth login
```

#### Azure CLI
```
fab auth login --azure-cli [--tenant <tenant_id>]
```

#### Service principal
```
# Service principal with secret
fab auth login -u <client_id> -p <client_secret> --tenant <tenant_id>

# Service principal with certificate
fab auth login -u <client_id> --certificate <path> --tenant <tenant_id>
```

#### Workload identity
```
fab auth login -u <client_id> --federated-token <token> --tenant <tenant_id>
```

**Parameters:**
Expand All @@ -32,7 +52,8 @@ fab auth login [-u <client_id>] [-p <client_secret>] [--federated-token <token>]
- `-p, --password`: Client secret for service principal. Optional.
- `--federated-token`: Federated token for workload identity. Optional.
- `--certificate`: Path to certificate file. Optional.
- `--tenant`: Tenant ID. Optional.
- `--azure-cli`: Use an existing Azure CLI login session as the token provider. Requires Azure CLI to be installed and logged in (`az login`). Optional.
- `--tenant`: Tenant ID. Optional. When used with `--azure-cli`, pins Fabric CLI to the specified tenant.

---

Expand Down Expand Up @@ -60,4 +81,4 @@ fab auth status

---

For more examples and detailed scenarios, see [Authentication Examples](../../examples/auth_examples.md).
For more examples and detailed scenarios, see [Authentication Examples](../../examples/auth_examples.md).
31 changes: 31 additions & 0 deletions docs/examples/auth_examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,36 @@ fab auth login
```


### Azure CLI Authentication

Reuse an existing Azure CLI session instead of requiring a separate Fabric CLI login. Useful when tools or scripts already have `az login` done (e.g., in development environments or CI/CD pipelines with Azure CLI pre-authenticated).

!!! info "Requires Azure CLI to be installed and logged in (`az login`)"

#### Log in using Azure CLI in interactive mode

```
fab auth login
? How would you like to authenticate Fabric CLI? Azure CLI (existing 'az login' session)
```

#### Log in using Azure CLI directly from command line

```
fab auth login --azure-cli
```

#### Log in using Azure CLI with a specific tenant

```
fab auth login --azure-cli --tenant <tenant_id>
```

!!! note "Tenant behavior"
- If `--tenant` is not specified, Fabric CLI captures and records the tenant from the current Azure CLI session at login time.
- Throughout the `fab` session, the Azure CLI's active tenant is checked against the recorded tenant. If you switch tenants in Azure CLI (e.g., `az login --tenant <other>`), Fabric CLI will raise a tenant mismatch error and require you to re-authenticate, e.g., `fab auth login --azure-cli`.


### Service Principal Authentication

!!! info "Requires 'Allow service principals to use Fabric APIs' tenant switch to be enabled in the admin portal"
Expand Down Expand Up @@ -81,6 +111,7 @@ Log in using service principal with federated credential directly
fab auth login -u <client_id> --federated-token <token> --tenant <tenant_id>
```


### Managed Identity Authentication

!!! info "Requires 'Allow service principals to use Fabric APIs' tenant switch must be enabled"
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ dependencies = [
"msal>=1.34,<2",
"msal_extensions",
"azure-core>=1.29.0",
"azure-identity>=1.15.0",
"questionary",
"prompt_toolkit>=3.0.41",
"cachetools>=5.5.0",
Expand Down
50 changes: 34 additions & 16 deletions src/fabric_cli/commands/auth/fab_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
def init(args: Namespace) -> Any:
auth_options = [
"Interactive with a web browser",
"Azure CLI (existing 'az login' session)",
"Service principal authentication with secret",
"Service principal authentication with certificate",
"Service principal authentication with federated credential",
Expand All @@ -27,12 +28,18 @@ def init(args: Namespace) -> Any:
# Clean up stale context files when logging in
Context().cleanup_context_files(cleanup_all_stale=True, cleanup_current=False)

if args.identity:
if getattr(args, "azure_cli", False):
FabAuth().set_access_mode("azure_cli", args.tenant)
FabAuth().set_azure_cli(args.tenant)
_acquire_default_access_tokens(FabAuth())
Context().context = FabAuth().get_tenant()
tenant_id = FabAuth().get_tenant_id() or "unknown"
fab_ui.print_grey(f"✓ Authenticated via Azure CLI (tenant: {tenant_id})")

elif args.identity:
FabAuth().set_access_mode("managed_identity")
FabAuth().set_managed_identity(args.username)
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)
_acquire_default_access_tokens(FabAuth())
Context().context = FabAuth().get_tenant()

elif any([args.username, args.password]):
Expand All @@ -54,9 +61,7 @@ def init(args: Namespace) -> Any:
FabAuth().set_spn(args.username, password=args.password)
elif args.federated_token:
FabAuth().set_spn(args.username, client_assertion=args.federated_token)
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)
_acquire_default_access_tokens(FabAuth())
Context().context = FabAuth().get_tenant()
else:
selected_auth = fab_ui.prompt_select_item(
Expand All @@ -69,10 +74,17 @@ def init(args: Namespace) -> Any:
try:
if selected_auth == "Interactive with a web browser":
FabAuth().set_access_mode("user", args.tenant)
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)
_acquire_default_access_tokens(FabAuth())
Context().context = FabAuth().get_tenant()
elif selected_auth.startswith("Azure CLI"):
FabAuth().set_access_mode("azure_cli", args.tenant)
FabAuth().set_azure_cli(args.tenant)
_acquire_default_access_tokens(FabAuth())
Context().context = FabAuth().get_tenant()
tenant_id = FabAuth().get_tenant_id() or "unknown"
fab_ui.print_grey(
f"✓ Authenticated via Azure CLI (tenant: {tenant_id})"
)
elif selected_auth.startswith("Service principal authentication"):
fab_logger.log_warning(
"Ensure tenant setting is enabled for Service Principal auth"
Expand Down Expand Up @@ -174,9 +186,7 @@ def init(args: Namespace) -> Any:
FabAuth().set_spn(client_id, password=client_secret)
elif federated_token:
FabAuth().set_spn(client_id, client_assertion=federated_token)
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)
_acquire_default_access_tokens(FabAuth())
Context().context = FabAuth().get_tenant()
elif selected_auth == "Managed identity authentication":
fab_logger.log_warning(
Expand All @@ -191,9 +201,7 @@ def init(args: Namespace) -> Any:

FabAuth().set_access_mode("managed_identity")
FabAuth().set_managed_identity(client_id)
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)
_acquire_default_access_tokens(FabAuth())
Context().context = FabAuth().get_tenant()

except KeyboardInterrupt:
Expand Down Expand Up @@ -266,15 +274,19 @@ def __mask_token(scope):

# Check login status
is_logged_in = fabric_secret != "N/A"
identity_type = auth.get_identity_type() or "N/A"
login_status = (
"✓ Logged in to app.fabric.microsoft.com"
if is_logged_in
else "✗ Not logged in to app.fabric.microsoft.com"
)
fab_ui.print_grey(login_status)
if identity_type == "azure_cli" and is_logged_in:
fab_ui.print_grey(f" Auth mode: Azure CLI (tenant: {tid})")

auth_data = {
"logged_in": is_logged_in,
"auth_source": identity_type,
"account": upn,
"principal_id": oid,
"tenant_id": tid,
Expand All @@ -291,3 +303,9 @@ def _get_token_info_from_bearer_token(bearer_token: str) -> Optional[dict[str, s
return FabAuth()._get_claims_from_token(
bearer_token, ["upn", "oid", "tid", "appid"]
)


def _acquire_default_access_tokens(auth: FabAuth) -> None:
auth.get_access_token(scope=fab_constant.SCOPE_FABRIC_DEFAULT)
auth.get_access_token(scope=fab_constant.SCOPE_ONELAKE_DEFAULT)
auth.get_access_token(scope=fab_constant.SCOPE_AZURE_DEFAULT)
Loading
Loading