Repository navigation
Support self-hosted UniFi OS Server - #1035
Merged
KodeStar merged 5 commits intoSep 16, 2026
Merged
Conversation
UniFi OS rejects a TOTP code that has already been used, so the dashboard refresh loop failed on every livestats poll that landed in an already-spent time step. Cache a successful result until the next code is due so at most one login happens per period; nothing is cached when no TOTP is configured. Verified against a live UniFi OS Server that a wrong or replayed code is rejected with 401/403 rather than 499, so report that case as a credential or TOTP failure and reserve the 499 message for a missing second factor. Also guard the stats response against a null connection result before decoding it.
The UniFi config toggle is labelled "Self Hosted", but it selects an API rather than a hosting model. Self-hosting is no longer the same thing as running the legacy UniFi Network application: a self-hosted UniFi OS Server (model UOSSERVER) speaks the UniFi OS API, so enabling the toggle - the intuitive choice for anyone hosting their own controller - points Heimdall at /api/login, which a UniFi OS device answers with a bare 401 without ever checking the credentials. The app then reports "Invalid credentials", which sends users looking for a password problem that does not exist. Relabel the toggle after the API it selects, and retry a refused login once against the other endpoint pair, keeping whichever one the device actually implements. Only a refused login retries, so a working configuration is unaffected, and the retry reuses the same attributes because the first request never reached authentication. The config key stays self_hosted so existing items keep working.
…-hosted Resolve the overlap in test() and livestats(): authenticate() now runs inside the InvalidArgumentException handling added for malformed TOTP URIs, and the TOTP result cache wraps the endpoint-fallback login.
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.
Summary
Make the enhanced UniFi app work against a self-hosted UniFi OS Server (model
UOSSERVER).The config toggle is labelled "Self Hosted", but it selects an API, not a hosting model. Self-hosting is no longer the same thing as running the legacy UniFi Network application — Ubiquiti now ships UniFi OS Server, which you host yourself and which speaks the UniFi OS API.
So the intuitive choice for anyone hosting their own controller is the wrong one. Enabling the toggle points Heimdall at
/api/login, and a UniFi OS device answers that path with a bare401without ever checking the credentials:Heimdall reports
Invalid credentials, which sends users looking for a password problem that does not exist. Note the wrong endpoint does not 404, so the status code alone cannot distinguish this from a genuine bad password.Changes
self_hosted, so existing items keep working.Only a refused login (
401/403/404) retries, so a working configuration is never affected. The retry reuses the same login attributes, because the first request never reached authentication — confirmed below. A retry is accepted on any status other than the same flat refusal, including499, so an account requiring two-factor authentication surfaces that instead of falling back to the misleading message.Validation
Against a live self-hosted UniFi OS Server (
UOSSERVER, UniFi OS 1.0.1) on:11443:Invalid credentialsUnexpected response (HTTP 499)— reaches the real endpointInvalid credentialsSuccessfully connected to UniFiSuccessfully connected to UniFiInvalid credentialslivestats()with the toggle onAlso verified that a login refused at the wrong endpoint does not consume a one-time 2FA code: the same code was accepted by
/api/auth/loginimmediately afterwards, and the resulting session returned200from the stats endpoint. That is what makes reusing the attributes for the retry safe.Static checks: PHP 8.2 syntax check, PHPCS with PSR-12 and the repository's multiline-function exclusion,
node testRunner.js,git diff --check.Notes
Two-factor authentication required.