explosivo22 / rinnaicontrolr

Python interface for the Rinnai Control-R API
Apache License 2.0
12 stars 3 forks source link

how does authentication work? #3

Closed dulitz closed 3 years ago

dulitz commented 3 years ago

I was looking at the auth flow and there's something very critical I don't understand. Let me describe the auth flow as I understand it:

  1. Client sends username and big A.
  2. Server sends back userid, salt, B, and a secret block.
  3. Client calculates a claim signature as a function of the userid, the password, big A, B, and the secret block, and sends the signature to the server.
  4. The server validates the signature and returns an access token.

The next thing I would expect is for the client to send the access token to the server along with each request. Sometimes it is sent in a cookie, but we don't use cookies here. Sometimes it is sent in postdata, but we don't do that. Most frequently it is sent in a header, for example:

    headers = { 'Authorization': 'Bearer %s' % self.config['smartthings']['accesstoken'] }

but we don't do that either.

So we've just executed this fancy zero-knowledge password proof in order to receive an access token, but the server is accepting our requests anyway without us including the access token?

I am missing something. Please tell me what I'm missing.

dulitz commented 3 years ago

Oh, upon reviewing Amazon's AppSync GraphQL developer guide, I now see that API_KEY authorization is used.

So it appears that this is an unauthenticated endpoint, and absolutely anyone on the internet can read all the information about me and my water heater, and also set new temperatures for me at any time, without needing to know my password, just the API_KEY which is in this codebase (and is the same for everyone).

Please confirm or refute my observation.

explosivo22 commented 3 years ago

Unfortunately this is what I was seeing as well and what I deciphered from monitoring the requests. I could not find where you had to send any information obtained from authentication back with any of the requests. I can do further testing to see what I find out. I have not checked what information is needed for scheduling or other functions. I only got as far as recirculation this time around.

dulitz commented 3 years ago

Thanks for the response. Please do more testing; I will also try to add a temperature-changing function to the API and see if that works.

According to the developer guide I linked to above, one can also use Cogito tokens with AppSync. (That's what your auth process retrieves: a Cogito token.) And one problem with the current API is that it is severely rate-limited. API_KEY authentication only works for one request every 15 minutes (from one IP address, probably). I'm trying to use the API to track usage, so that's not as often as I'd like, so I might try passing the Cogito token in a header to the API request, to see if that gives a higher rate limit.

Having an insecure API like this is pretty awful. Do you have any contacts at Rinnai that you can complain to so they fix it? They should not allow API_KEY authentication to their AppSync endpoing, only Cogito User Pool authentication.

explosivo22 commented 3 years ago

I have made a new branch async-main-dev where I am working on making the API function better and making everything asynchronous. You can check that out. This version does have starting/stopping recirculation and also setting the temperature.

I agree with you fully that this is a very insecure API. I don't have a contact, but I will be reaching out to them in every was I can.

I have had a little time to dig a little further into this and there is a very hidden PATCH API call that can be used with the header:

Authorization: Bearer {IdToken}

I do not store the ID token as this was the first I had seen this come across whether that was through updates from their end or not. You can set multiple values on the device at the same time so multiple calls would not have to be made. There is still a little more functionality that I need to investigate that looks like they have added that maybe will add security to the calls of getting information and setting information.

dulitz commented 3 years ago

Okay, I'll be sending you another group of pull requests for the synchronous (mainline) code, focusing on making authentication clearer in the code.

I tested trying to authenticate without the api key (just with the Cogito access token) and it fails. I will reach out to them too.

Wanna check in a ROADMAP.md file? I'll add to it. Then I can close this issue, because I think we've figured out how authentication "works."

dulitz commented 3 years ago

Oh, and regarding aysncio: do it if you want to learn asyncio. :) But so far, this is an API where synchronous is fine most of the time. Async is ideal if something can happen remotely (e.g. there is a persistent connection and occasionally the server will write to it) and you don't want to keep polling for it, or if server responses can sometimes be really slow.

The api-key authentication rate-limits us to one query every 15 minutes. That's not good for polling at all. It would be better if their API offered a way to subscribe to state-change events (such as combustion starting or stopping, or recirculation starting or stopping, etc.). Do you have a way of observing their app's network traffic? I suspect their app already does this somehow...

explosivo22 commented 3 years ago

I have determined the Rinnai at some point does use the Cognito get_id and the Cognito get_credentials_for_identity. They then allow you to do a patch request updating multiple values at once passing along some of the keys and tokens. They also allow you to pull device information passing the keys and tokens. I will have more to come once I can dig into each request and response and put together the whole process.

explosivo22 commented 3 years ago

Wanna check in a ROADMAP.md file? I'll add to it. Then I can close this issue, because I think we've figured out how authentication "works."

I have also created a ROADMAP.md file and put just a little something in it. Feel free to change as you like.

dulitz commented 3 years ago

Great, please do document the updating of multiple values once you are able to dig in. Feel free to comment on my advisory doc before I pass it along privately to people at Rinnai, and possibly others depending on how they respond or don't.