Skip to content

Network: API-key auth reaches only 2 of ~25 managers — SSO/MFA accounts cannot use most tools #566

Description

@the78mole

Summary

With only UNIFI_NETWORK_API_KEY configured, most Network tools fail. The API-key path exists — AuthProvider.get_api_key_session() in unifi_core/auth.py — but is used by exactly two managers, dpi_manager.py and firewall_manager.py. Every other manager reaches the controller through aiounifi, which performs a session login with username and password.

On a Ubiquiti SSO account with MFA enabled, that login cannot succeed:

Failed to list networks: Not connected to controller:
last connection attempt failed: SSO MFA required but no totp_secret configured

The sample config already labels the key as "experimental — read-only, subset of tools", so the limitation is known. This issue documents the concrete gap and proposes a direction.

Environment

unifi-network-mcp 0.27.3
unifi-core / unifi-mcp-shared / aiounifi 0.4.30 / 0.6.9 / 93
Controller self-hosted, UniFi Network API 10.5.67
Auth Ubiquiti SSO account with MFA; API key from Control Plane → Integrations

What I verified

1. The API key works against the official Integration API.

curl -sk -H "X-API-KEY: $KEY" -H "Accept: application/json" \
  https://<host>/proxy/network/integration/v1/sites
# HTTP 200 → {"offset":0,"limit":25,"count":2,"totalCount":2,"data":[…]}

Same for /sites/{siteId}/networks, which returns vlanId and name per network.

2. In the installed package, the API-key path is referenced in only four files.

AuthProvider, AuthMethod and get_api_key_session appear in:

  • unifi_core/auth.py
  • unifi_core/network/managers/dpi_manager.py
  • unifi_core/network/managers/firewall_manager.py
  • unifi_network_mcp/runtime.py (wiring)

Meanwhile 25 modules import aiounifidevice_manager, client_manager, acl_manager, dns_manager, client_group_manager, event_manager, and so on.

3. There is no global switch.

def from_string(cls, value: str | None) -> "AuthMethod":
    if value is None:
        return cls.LOCAL_ONLY

AuthMethod is selected per call site, and no environment variable maps onto it. An operator who supplies only an API key gets an MFA error from deep inside aiounifi rather than a clear statement that the tool needs local auth.

Why it matters

Ubiquiti pushes cloud SSO, and MFA is often enforced. The workarounds today are both unattractive:

  • Store the account's TOTP seed next to the password — which removes the benefit of the second factor entirely.
  • Create a local-only admin account — works, but is another credential to manage, and in some deployments it is not desirable or permitted.

An API key is the natural fit: scoped, revocable on its own, and unaffected by MFA.

What the Integration API already covers

From the controller's own reference (Control Plane → Integrations, API 10.5.67):

Application Info · Sites · Devices (list, details, statistics, adopt/unadopt, device actions, port actions) · Clients (list, details, actions) · Networks (CRUD and references) · WiFi Broadcasts · Hotspot vouchers · Firewall zones and policies · ACL rules · Switching (stacks, MC-LAG, LAGs) · DNS policies · Traffic Matching Lists · Supporting resources (WAN interfaces, site-to-site VPN, VPN servers, RADIUS profiles, device tags, DPI, countries)

That appears to cover the majority of the read-oriented tools, and a good share of the write operations too.

Proposal

  1. Extend the existing pattern to the inventory managers firstdevice_manager, client_manager, and the network/WLAN listing paths — using await self._auth.get_api_key_session() exactly as dpi_manager and firewall_manager already do.
  2. Expose the selection as configuration, e.g. UNIFI_NETWORK_AUTH_METHOD=api_key_only|either|local_only, so a key-only setup fails fast with a clear message instead of an MFA error from aiounifi.
  3. Mark tools that genuinely require a legacy endpoint in the tool index, so a key-only deployment can see up front what is unavailable rather than discovering it at call time.

I am happy to work on step 1 if that direction is welcome — I have a fork at the78mole/unifi-mcp and a controller to test against.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions