Strange Device Token Behavior

I am using the https://industrial.ubidots.com/api/v2.0/devices/{device_id}/variables/ API endpoint to query for device variables. With X-Auth-Token set to an account token, I get a response with code 200 and the data.

With a device token, I get a response with code 404 and the following response:

{
    "code": 404001,
    "message": "The request was not found."
}

With a made-up, gibberish token that doesn’t exist, I get a code 401 unauthorized.

My guess is that device tokens are not allowed to access variable data. However, on the help page for device tokens, it is not explicitly stated that a device token isn’t allowed permissions to get a variable, but only denied permissions for create, edit, and delete: Security: Managing Device Tokens | Ubidots Help Center

If it were the case that device tokens are not allowed to have read-only access to variables, it would make more sense to return a 401 unauthorized response. Which is why I am confused and writing this thread.

Hi @dan,

For the sake of clarification, it is worth noting for the audience here that the below response happens when trying to read the variables of a device with a Device token that doesn’t belong to the device itself, but to another one.

For example, if I try to read the variables of a device labelled demo but with a token from another device, as follows:

curl -X GET 'https://industrial.api.ubidots.com/api/v2.0/devices/~demo/variables' \
-H 'X-Auth-Token: <DEVICE-TOKEN>' #  <DEVICE-TOKEN> from another device

I’ll get the response you mentioned.

Apart from this fact, let us forward this information to our Dev team and get their feedback. For the time being, your suggestion to return a 401 error response code makes sense when using a wrong Device token, but let’s hear them out.

Thank you for your input!

I did some testing today, and it seems that this only affects certain devices. With other devices, requests work fine with the device token displayed on the page.

First, I tested by hand. Some devices worked, others did not. Then I wrote a script to see which ones worked and which ones didn’t:

(tokens and device IDs redacted in white)

The validation was done through iterating devices, fetching the device token via an API call, then making a request to the variable endpoint with the fetched device ID and token.

There isn’t any human error involved here, since the device IDs and tokens were retrieved via API and some worked but others did not.

Hi @dan,

Thank you for the commitment of testing this rigorously. With this in mind:

  1. Do you mind sharing your code? The whole fact that reading the /api/v2.0/devices/<device_key>/variables endpoint works with some devices while using their respective Device token, and not with some others, seems out place. They all should render the same behavior, and that’s why we’d like to test it from our end.

  2. After sharing this case with our back-end team, the reason why the API responds with a 404 (not found) error code when using a Device token for a device different from that of the token, is security: if the API would reply with a 403 (permissions error), it would mean the device exists, which gives more information than the necessary to what could be malicious doers, whereas a 404 adds a layer of security by not disclosing whether the device exists or not.

Sure thing, here’s the Python script. You’ll need the requests library to run the code:

import requests

TOKEN = "My Account Token"

HEADERS = {
    "X-Auth-Token": TOKEN
}

def validate_token(device_id, token):
    print("Validating token for " + device_id)

    headers = {
        "X-Auth-Token": token
    }

    resp = requests.get(f"https://industrial.ubidots.com/api/v2.0/devices/{device_id}/variables/", headers=headers)

    if (resp.ok):
        print(f"Token {token} for {device_id} validated.")
    else:
        print(f"Token {token} for {device_id} failed validation with {resp.status_code}")

    return resp.ok

def get_device_token(device_id):
    print("Getting device token for " + device_id)

    resp = requests.get(f"https://industrial.api.ubidots.com/api/v1.6/datasources/{device_id}/tokens", headers=HEADERS).json()

    return resp["results"][0]["token"]

def get_all_devices():
    devices = []
    
    next = "https://industrial.api.ubidots.com/api/v2.0/devices/"
    
    while next:
        print("Making request to " + next)
        data = requests.get(next, headers=HEADERS).json()
        next = data["next"]
        devices.extend(data["results"])

    return devices

devices = get_all_devices()

for device in devices:
    token = get_device_token(device["id"])
    validate_token(device["id"], token)

I completely understand returning a 404 for security reasons, but it was returning 404 with what was supposed to be a valid token so I was compelled to make a forum post.

If need be, I can supply a CSV of device IDs and whether or not the token retrieved for them was valid.