Long-lived access token issued by non-admin does not work

Hi. I’m really new to HA and stumbled upon this, which had me scratch my head for a day.

Goal, problem and solution…
My goal was to update a sensor state (state of connection to a specific bluetooth device) from my 2 users (user1 and user2) mobile phones via the Tasker app. I used this guide and in particular this commenters “HA set-state” tasks, and it worked like a charm on my (user1) account, which is an admin account.

However, trying to do the same with the non-admin account (user2), using user2 long-lived token, gave me an error 401 unauthorised numerous times. The problem solved itself when I made said account an admin.

System:
version: core-2021.12.4
installation_type: Unknown
dev: false
hassio: false
docker: true
user: abc
virtualenv: false
python_version: 3.9.5
os_name: Linux
os_version: 5.4.0-91-generic
arch: x86_64

Tl;dr: long lived access token issued by non-admin user gives an “error 401 unauthorized”, problem disappears when said user is made an admin.

Question: is this by design or is it an error?

Yes it is by design, the code restricting this API to admins is here. Ideally it would return a 403 Forbidden to make it clear it knows who you are and you aren’t allowed to do what you’re trying to do instead of a 401 Unauthorized which seems like it could be a bug (like it misunderstood your credentials). But it is definitely not a bug and by design.

It also makes a lot of sense. Directly setting values into the state machine is an advanced task primarily for debugging purposes. Hence why the UI only shows that option in developer tools, a panel only available for admins.

Also as an aside, I used to do something very similar but no longer need to. The native mobile app has a wide array of sensors including all the information you could need on what bluetooth devices are currently connected. I’ve dropped nearly all my tasker automation around HA since the mobile app has covered nearly all of those use cases (basically the only one remaining is turning on and off wireguard when I leave my home wifi since that uses a protected intent, something the HA app cannot call). Have you checked it out? I find it a lot easier to have all my automation in one place personally rather then split between tasker and HA.

Aha, yes it sort of makes sense. Though several things had me thoroughly confused. First off I did not find anything about this in the documentation revolving around long-lived tokens or the API, that you had to be an admin to issue the token. Also, I assumed that when the regular user is able to issue a long lived token, he’d be able to use it, but it might be for other use cases? Third, I think it might be a security issue that I have to give an admin-level token to a regular user to have this working, or make said user an admin. Is there any way I can restrict the use of a particulsr token to just this state?

Yes I do have the companion app installed, though the two things I needed right off the bat was GPS location and the specific name of the connected bluetooth device, which is not among the 64 sensors available on any of these particular phones.

Thank you so much for the answer :grinning_face_with_smiling_eyes:

Yea you’re right it’s not documented, I had to dig into the code to find the hook. The REST API doc is here but it doesn’t mention anything about which APIs are admin only that I can see.

A regular user can use a long-lived access token though, it just won’t work on that particular API. Most of the API is not admin-only, users are free to get states and even call services I believe, just set state is locked down due to its nature. Maybe others, I’m not sure.

Not that I can see, its just a blanket check on the API. The doc for developers on what they need to know about permissions control is here. As you can see the entire article is prefixed with a “this is experimental” caveat. There’s still a lot to be fleshed out in that area.

Hm well yea I suppose it doesn’t have the name but it does have the mac address for each connected device if you enable this one:

Then you can grab that information from the connected_paired_devices attribute of the bluetooth_connection sensor. I realize the mac address isn’t the name but its actually better for automation since it doesn’t change. Also if you do want to show the name can just make a sensor that converts it like:

template:
- sensor:
    - name: Bluetooth device connected to my phone
      unique_id: bluetooth_device_connected_to_my_phone
      state: >-
          {% set mac_name_map = {
                "AA:AA:AA:AA:AA:AA": "Headphones",
                "BB:BB:BB:BB:BB:BB": "Car",
                ...
              } %}
          {{ mac_name_map[
               state_attr('sensor.my_phone_bluetooth_connection', 'connected_paired_devices') | first
             ] }}

Just a thought. Although obviously if you change the name of the device you’ll have to update here so I guess depends on how often you do that.

Wow thank you for this thorough reply!

Aha, so I sort of stumbled into the only use case where this is an issue, haha typical.

This is probably something where I’ll be in over my head then. But nice to know it’s being worked on.

Of course! Yes that is much better to be honest. The only reason I needed the name was to distinguish it from any of the other bluetooth devices. But MAC is better as it is (quasi-)unique, as you could run into problems with identical names.

I will definitely use the companion app for as much as possible, as you say, it is easier to have it all in one place. First I sent service calls from Tasker, then realising It’d be a simpler action to only send a state and let the server handle the service calls, and now this, using the companion app. Haha it’s an iterative journey for sure.

Again thanks a lot for the thorough replies. Merry Christmas!

1 Like