If you’re a developer working with Tado’s unofficial REST APIs, you may have recently come across an important update from Tado regarding authentication. In a recent support article, Tado has requested that developers modify their authentication mechanisms to ensure secure and compliant access to their APIs: https://support.tado.com/en/articles/8565472-how-do-i-authenticate-to-access-the-rest-api
This change is critical for maintaining the integrity of Tado’s systems and protecting user data. I’ll break down what this means for developers and how to adapt to the new requirements.
Tado’s REST APIs have been a popular tool for developers looking to integrate smart home functionality into their applications. However, as with any system, security is an ongoing concern. Tado has identified potential vulnerabilities in the way some developers are handling authentication, particularly when using unofficial APIs. To address these concerns, Tado is now enforcing stricter authentication protocols to prevent unauthorized access and ensure that only legitimate requests are processed.
This move is not uncommon in the tech world. As APIs become more widely used, companies often need to tighten security measures to protect their infrastructure and users. For developers, this means staying up-to-date with these changes and adapting their code accordingly.
The primary change revolves around how developers authenticate with Tado’s APIs. Previously, some developers may have relied on less secure methods, such as hardcoding credentials or using outdated authentication flows. Tado is now requiring developers to implement a more robust and secure authentication mechanism.
While the specifics of the new authentication process may vary depending on your implementation, here are some general guidelines to follow:
Use OAuth 2.0: Tado requires using OAuth 2.0 device code flow for authentication, which is a widely adopted standard for secure API access. OAuth 2.0 provides a secure way to handle tokens and ensures that credentials are not exposed in requests.
Avoid Hardcoding Credentials: Hardcoding usernames, passwords, or tokens in your code is a significant security risk. Instead, use environment variables or secure credential storage solutions to manage sensitive information.
Implement Token Refresh: Access tokens typically have a limited lifespan. Make sure your application can handle token expiration by implementing a token refresh mechanism. This ensures uninterrupted access to the API without requiring manual intervention.
If you’re currently using Tado’s unofficial APIs, it’s time to review your authentication process and make the necessary changes to implement device code flow. This flow is designed for devices that lack a keyboard or easy input method, such as smart thermostats or mobile apps. Here’s how it works:
$response = $this->client->post('https://login.tado.com/oauth2/device_authorize',
[ 'form_params' => [ 'client_id' => '1bb50063-6b0c-4d11-bd99-387f4a91cc46
‘,
'scope' => 'offline_access', ], ]);
{"device_code":"ftcrinX_KQaXUNI1wkh-5zxFmmYOUug43SAYWORs1AU","expires_in":300,
"interval":5,"user_code":"9HAZP1",
"verification_uri":"https://login.tado.com/oauth2/device",
"verification_uri_complete":"https://login.tado.com/oauth2/device?user_code=9HAZP1"}
$response = $this->client->post('https://login.tado.com/oauth2/token', ['form_params' => ['client_id' => '1bb50063-6b0c-4d11-bd99-387f4a91cc46',
'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code',
'device_code' => $device_code]]);
[access_token] => eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImd0eSI..............................KcYbYQ
[expires_in] => 599
[refresh_token] => 6Vu1vQadysY-1G6naR8gdp_y-AgFtakb75C7KVK5-uUxgbM3EWHTza2e2D6ZD81W
[refresh_token_id] => 9fa5bb86-8d55-4178-9268-f13bbd1bc1a5
[scope] => offline_access
[token_type] => Bearer
[userId] => 595e1511-078f-8010-332a-0adc13930002
Access tokens have a limited lifespan (10 minutes). Make sure your application can handle token expiration by implementing a token refresh mechanism.
$response = $this->client->post('https://login.tado.com/oauth2/token', ['form_params' => ['client_id' => '1bb50063-6b0c-4d11-bd99-387f4a91cc46','grant_type' => 'refresh_token','refresh_token' => $refresh_token]]);
You can take a look at my working implementation here: https://github.com/dam2k/tadoapi
Enjoy your smart home!
What happens when you publish a message to an exchange in RabbitMQ with the wrong topic, or better, routing key? What happens if you send a message to the broker in a queue with a TTL policy or the TTL property is in the message itself and that TTL expires? What happens when a consumer discard your message got from the queue with no republish? What if a queue overflows due to a policy?
It’s simple, the broker will simply discard your message forever.
If this thing will make you mad like it does to me this blog article is for you. Here I will tell you how to create a simple tree of queues, DLX and policies to overcome this problem.
I think that starting with commands and examples is better than 10000 written words, and since I don’t have any ads on my blog I don’t have to write a long article to get money from the ads, so here we are.
I consider your RabbitMQ installation and your admin account ready, so we start with the commands.
# Create your VirtualHost
rabbitmqctl add_vhost vhtest --description "Your VH" --default-queue-type classic
# Give your admin user permissions to to everything on your virtualhost
rabbitmqctl set_permissions --vhost vhtest admin '.*' '.*' '.*'
# Create the user that will publish messages to the exchange
1
rabbitmqctl add_user testuserpub yourpassword
# Create the user that will subscribe to your queue to read messages
rabbitmqctl add_user testusersub yourpassword2
Now, we have 3 users (admin, testuserpub and testusersub) and a virtualhost (vhtest). We are ready to create 2 DLX, one to handle overflow, expired TTL and discarded messages, the other to handle messages sent with the wrong routing key. A DLX (or Dead Letter Exchange) is a particular exchange that is designed to handle dead lettered (discarded) messages.
# Create the DLX to handle overflowed, expired or discarded by consumers
rabbitmqadmin declare exchange --vhost=vhtest name=DLXexQoverfloworttl type=headers internal=true
# Create the DLX to handle messages with wrong routing key
rabbitmqadmin declare exchange --vhost=vhtest name=DLXexQwrongtopic type=fanout internal=true
We’ll now declare and bind queues to the first DLX using three different policies
rabbitmqadmin declare queue --vhost=vhtest name=DLXquQoverflow
rabbitmqadmin declare queue --vhost=vhtest name=DLXquQttl
rabbitmqadmin declare queue --vhost=vhtest name=DLXquQrejected
rabbitmqadmin declare binding --vhost=vhtest source=DLXexQoverfloworttl destination=DLXquQoverflow arguments='{"x-first-death-reason": "maxlen", "x-match": "all-with-x"}'
rabbitmqadmin declare binding --vhost=vhtest source=DLXexQoverfloworttl destination=DLXquQttl arguments='{"x-first-death-reason": "expired", "x-match": "all-with-x"}'
rabbitmqadmin declare binding --vhost=vhtest source=DLXexQoverfloworttl destination=DLXquQrejected arguments='{"x-first-death-reason": "rejected", "x-match": "all-with-x"}'
And now we’ll declare and bind queues to the second DLX to handle messages with wrong topic (routing key)
rabbitmqadmin declare queue --vhost=vhtest name=DLXquQwrongtopic
rabbitmqadmin declare binding --vhost=vhtest source=DLXexQwrongtopic destination=DLXquQwrongtopic
Now we have 1 DLX with 3 queues and another DLX with 1 queue bound. The first will route expired, discarded and overflowed messages to the respective queues (DLXquQttl, DLXquQoverflow, DLXquQrejected), the second will route messages with invalid routing key to the respective queue (DLXquQwrongtopic).
Now we are going to create our main queue and the normal Exchange that will send message to it
rabbitmqadmin declare queue --vhost=vhtest name=quQ
rabbitmqadmin declare exchange --vhost=vhtest name=exQ type=direct
In this example, we want to route all messages with routing key NBE
rabbitmqadmin declare binding --vhost=vhtest source=exQ destination=quQ routing_key=NBE
We now want to create the policy that is needed to associate the wrong topic DLX to our main exchange
rabbitmqctl set_policy --vhost vhtest wrongtopicQ1 "^exQ$" '{"alternate-exchange":"DLXquQwrongtopic"}' --apply-to exchanges
This is an example policy to set limits to 100 messages, 1073741824 bytes, 30 seconds TTL to the quQ queue.
rabbitmqctl set_policy --vhost vhtest shorttimedqunbe '^quQ$' '{"max-length":100,"max-length-bytes":1073741824,"message-ttl":30000,"overflow":"reject-publish-dlx","dead-letter-exchange":"DLXexQoverfloworttl"}' --priority 0 --apply-to queues
Going to give proper permissions to our publish and subscriber users. The user testuserpub can only write to its exchange, while testusersub can read from its queue. No other permissions here.
rabbitmqctl set_permissions --vhost vhtest testuserpub '' '^exQ$' ''
rabbitmqctl set_permissions --vhost vhtest testusersub '' '' '^quQ$'
Mission complete. Please try this at home and write to the comments below! Happy RabbitMQ hacking!