Skip to content

Fix: REST API sends 'Bearer empty' when token not configured in addon…#742

Merged
davidusb-geek merged 1 commit into
davidusb-geek:masterfrom
who2us:master
Mar 10, 2026
Merged

Fix: REST API sends 'Bearer empty' when token not configured in addon…#742
davidusb-geek merged 1 commit into
davidusb-geek:masterfrom
who2us:master

Conversation

@who2us
Copy link
Copy Markdown
Contributor

@who2us who2us commented Mar 9, 2026

Fix: REST API authentication fails when token is set to "empty" in addon mode

Problem

When running EMHASS as a Home Assistant addon with hass_url and long_lived_token
both set to "empty" (the documented default for addon users), the REST API calls
fail with a 401 Unauthorized error.

Root Cause

In retrieve_hass.py, the get_ha_config() method correctly checks for "empty"
token and skips HA — but _get_data_rest_api() does not perform this check.
It blindly uses self.long_lived_token directly in the Authorization header:

# BEFORE (broken) - sends "Bearer empty" as the auth header
headers = {
    "Authorization": header_auth + " " + self.long_lived_token,
    ...
}

This results in Authorization: Bearer empty being sent to the HA API, causing a
401 Unauthorized rejection every time.

Additionally, get_ha_config() would skip HA entirely when token is "empty",
rather than falling back to the SUPERVISOR_TOKEN environment variable that
Home Assistant injects automatically into addon containers.

Fix

Both get_ha_config() and _get_data_rest_api() now:

  1. Check if long_lived_token is None, empty, or "empty"
  2. If so, fall back to the SUPERVISOR_TOKEN environment variable
    (automatically injected by HA supervisor into addon containers)
  3. Only skip HA entirely if no valid token can be found from either source
# AFTER (fixed) - correctly uses SUPERVISOR_TOKEN when long_lived_token is "empty"
token = self.long_lived_token
if not token or token == "empty":
    token = os.getenv("SUPERVISOR_TOKEN", "")
    if token:
        self.logger.debug("Using SUPERVISOR_TOKEN from environment for REST API authentication.")
    else:
        self.logger.error("No valid authentication token found.")
        return False
headers = {
    "Authorization": header_auth + " " + token,
    ...
}

Impact

  • Fixes addon users who follow the documented setup of leaving hass_url and
    long_lived_token as "empty" — this is the default recommended configuration
  • No impact on users who provide explicit tokens (those still work as before)
  • No impact on Docker standalone users (SUPERVISOR_TOKEN won't exist, so explicit
    token is still required)

Testing

Tested on Home Assistant OS 2026.3.1 with EMHASS addon v0.17.0 running as a
supervised addon with both hass_url and long_lived_token left at default "empty".

Summary by Sourcery

Handle Home Assistant addon authentication correctly when long-lived tokens are unset or configured as "empty" by falling back to supervisor-provided credentials instead of sending invalid REST headers.

Bug Fixes:

  • Prevent REST API requests from using an invalid "Bearer empty" authorization header when no explicit long-lived token is configured.
  • Allow Home Assistant config retrieval and REST data fetching to fall back to the SUPERVISOR_TOKEN environment variable when the long-lived token or hass_url are unset or set to "empty".
  • Ensure HA config retrieval is skipped only when no usable authentication token can be resolved from either configuration or environment.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Mar 9, 2026

Reviewer's Guide

Updates Home Assistant REST API integration to resolve tokens and URLs more robustly, using SUPERVISOR_TOKEN and a default supervisor URL when addon configuration fields are set to "empty", and ensures REST calls no longer send an invalid "Bearer empty" Authorization header.

Updated class diagram for RetrieveHass token and URL handling

classDiagram
    class RetrieveHass {
        - hass_url : str
        - long_lived_token : str
        - ha_config : dict
        - logger

        + get_ha_config() async
        + _get_data_rest_api(var_list, start, end, header_auth, header_accept) async
    }

    %% get_ha_config behavior
    class GetHAConfigLogic {
        + resolve_token(long_lived_token) str
        + resolve_url(hass_url) str
        + build_headers(token, header_auth, header_accept) dict
    }

    %% _get_data_rest_api behavior
    class RestAPILogic {
        + resolve_token(long_lived_token) str
        + build_headers(token, header_auth, header_accept) dict
    }

    RetrieveHass --> GetHAConfigLogic : uses
    RetrieveHass --> RestAPILogic : uses
Loading

File-Level Changes

Change Details Files
Unify token and URL resolution logic in Home Assistant config retrieval to support addon defaults and supervisor-provided values.
  • Introduce local token resolution in get_ha_config, falling back to SUPERVISOR_TOKEN when long_lived_token is None, empty, or set to "empty"
  • Introduce local URL resolution in get_ha_config, falling back to the default supervisor hass_url when hass_url is None, empty, or set to "empty"
  • Skip Home Assistant config retrieval only when no valid token is available after env lookup, while still initializing an empty ha_config
  • Use the resolved token and URL variables when building headers and request URLs, including the trailing-slash warning and supervisor-path special case
src/emhass/retrieve_hass.py
Prevent REST API data calls from sending an invalid "Bearer empty" Authorization header and support supervisor token fallback.
  • Add token resolution in _get_data_rest_api mirroring get_ha_config, preferring long_lived_token and falling back to SUPERVISOR_TOKEN when necessary
  • Log a debug message when SUPERVISOR_TOKEN is used and an error plus early return when no valid token can be found
  • Use the resolved token instead of long_lived_token when constructing Authorization headers for REST API calls
src/emhass/retrieve_hass.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In _get_data_rest_api the function is annotated to return None but now returns False in an error case; consider either normalizing this to always return None or updating the type/usage so callers don’t have to handle mixed None/bool return types.
  • The log message in get_ha_config still says 'No Home Assistant URL or Long Lived Token found' even though the early-exit condition only checks for a missing token (URL may be resolved from hass_url); updating this message will make troubleshooting clearer.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `_get_data_rest_api` the function is annotated to return `None` but now returns `False` in an error case; consider either normalizing this to always return `None` or updating the type/usage so callers don’t have to handle mixed `None`/`bool` return types.
- The log message in `get_ha_config` still says 'No Home Assistant URL or Long Lived Token found' even though the early-exit condition only checks for a missing token (URL may be resolved from `hass_url`); updating this message will make troubleshooting clearer.

## Individual Comments

### Comment 1
<location path="src/emhass/retrieve_hass.py" line_range="194-196" />
<code_context>
+            url_to_use = hass_url  # default: http://supervisor/core/api
+
+        # If we still have no token after checking env, skip HA config retrieval
+        if not token:
             self.logger.info(
                 "No Home Assistant URL or Long Lived Token found. Using only local configuration file."
             )
</code_context>
<issue_to_address>
**issue:** The log message no longer matches the condition it’s guarding, which may mislead users when only the token is missing.

Since the early return now depends only on `not token`, the URL is guaranteed to be set (from `self.hass_url` or the default `hass_url`). The current log implying both URL and token are missing is therefore misleading. Please update the message to reflect only the missing token, or conditionally log based on which value is actually absent.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +194 to 196
if not token:
self.logger.info(
"No Home Assistant URL or Long Lived Token found. Using only local configuration file."
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.

issue: The log message no longer matches the condition it’s guarding, which may mislead users when only the token is missing.

Since the early return now depends only on not token, the URL is guaranteed to be set (from self.hass_url or the default hass_url). The current log implying both URL and token are missing is therefore misleading. Please update the message to reflect only the missing token, or conditionally log based on which value is actually absent.

@who2us
Copy link
Copy Markdown
Contributor Author

who2us commented Mar 9, 2026

Sorry guys 1st time uploading, and Claude is helping.
feel free to retest, and change as needed, but the EMHASS isnt working for me (as a new install)

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 9, 2026

Codecov Report

❌ Patch coverage is 47.61905% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.92%. Comparing base (eb44041) to head (c8646a0).
⚠️ Report is 7 commits behind head on master.

Files with missing lines Patch % Lines
src/emhass/retrieve_hass.py 47.61% 11 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #742      +/-   ##
==========================================
- Coverage   81.03%   80.92%   -0.11%     
==========================================
  Files          10       10              
  Lines        5611     5626      +15     
==========================================
+ Hits         4547     4553       +6     
- Misses       1064     1073       +9     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@davidusb-geek davidusb-geek merged commit 94c3a27 into davidusb-geek:master Mar 10, 2026
15 of 19 checks passed
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