fronzbot / blinkpy

A Python library for the Blink Camera system
MIT License
525 stars 111 forks source link

PIN Code Rejection #917

Closed Judas2016 closed 3 weeks ago

Judas2016 commented 2 months ago

I've been using the library since .20 with no problems. This week I started getting challenged for PIN code. I ran the code in interactive debug mode and plugged in the PIN code and it's being rejected - I'm getting the error "unable to access https://...." with the account/homescreen end point. Below is the code, I set the PIN variable before stepping into the if statement, same result. Anyone have ideas what changed or I'm doing wrong? Also, this code runs via Lambda service in the cloud - i'm running in local debugger and having problems - no idea how i'll make this work again as a lambda function....

blink = Blink(session=ClientSession()) auth = Auth({"username": f"{username}", "password": f"{password}"}, no_prompt=True) blink.auth = auth await blink.start() if (pin != None): await auth.send_auth_key(blink, pin) await blink.setup_post_verify() camera = blink.cameras[devicename] after calling the setup_post_verify I get the following error: ERROR - Unable to retrieve cameras from response {'range_days': 7, 'reference': {'usage': 400}, 'networks': [{'network_id': 292636, 'name': 'XXX', 'cameras': []}]}

fronzbot commented 2 months ago

It's possible Blink is changing their login endpoint. Whenever they do that it has historically been a slow rollout with some user being impacted before it goes to everyone. It tends to be an easy fix, but is painful until the new endpoint is uncovered. Thanks for reporting this, hopefully it's not too much of a wait before we figure out the new endpoint (assuming that's root cause).

mkmer commented 2 months ago

I believe they have been reworking things recently - I noticed the phone app now has a delete all function (in the last month or so). We will probably need someone to do the MITM on the phone app and capture what the new endpoints/format is. There have been many complaints over on HomeAssistant related to 2FA issues recently.

markeff commented 2 months ago

To add to the thread, I'm experiencing the same issue as reported (I'm a user based in the UK). Started having a problem at 2am on Wednesday morning (17th). Have run script to request/send PIN code (which is sent by SMS) but get the same issue "Unable to retrieve cameras from response"

Tombost commented 2 months ago

Hello I'm not using blinkpy but the original HTTP Rest from MattTW and I got the same problem. Everything was working since several month, but since 5 days it's not working anymore, my token seems to be invalid after 1 day, check here : https://github.com/MattTW/BlinkMonitorProtocol/issues/73

Tombost commented 2 months ago

using apk man in the middle I just see that the pin verify is using: POST /api/v5/accounts/{AccountID}/users/{UserID}/clients/{{clientID}}/client_verification/pin/verify and not POST /api/v4/account/{AccountID}/client/{ClientID}/pin/verify

Need to confirm if my token is still ok tomorrow

fronzbot commented 2 months ago

Thanks @Tombost

Need to update the API here with the new url and release an rc version to make sure it works. Looks like we're missing the {UserID} field though, so need to understand how to reliably retrieve that (is that just the Blink username?)

https://github.com/fronzbot/blinkpy/blob/a9758155276fbc9e24336334498e1402e3ab96a2/blinkpy/api.py#L64-L67

Tombost commented 2 months ago

Thanks @Tombost

Need to update the API here with the new url and release an rc version to make sure it works. Looks like we're missing the {UserID} field though, so need to understand how to reliably retrieve that (is that just the Blink username?)

https://github.com/fronzbot/blinkpy/blob/a9758155276fbc9e24336334498e1402e3ab96a2/blinkpy/api.py#L64-L67

You got the userID when you ask https://rest-prod.immedia-semi.com/api/v5/account/login With your credential : 325509477-66df40ad-df92-442d-b551-c24504a06449

fronzbot commented 2 months ago

@Judas2016 try pip install blinkpy==0.23.0b7 and let me know if your issue is fixed. This pre-release version has the above API endpoint fix.

Judas2016 commented 2 months ago

I am getting a different error now:

2024-04-27 16:12:46,624 - auth.py.query(228) - ERROR - Unable to access https://rest-u056.immedia-semi.com/api/v3/accounts/xxxx3/homescreen after token refresh. 2024-04-27 16:13:19,635 - auth.py.send_auth_key(244) - ERROR - Did not receive valid response from server. Error: 'valid' 2024-04-27 16:13:20,930 - auth.py.refresh_token(116) - INFO - Token expired, attempting automatic refresh. 2024-04-27 16:13:20,932 - auth.py.login(94) - INFO - Attempting login with https://rest-prod.immedia-semi.com/api/v5/account/login 2024-04-27 16:13:21,986 - auth.py.query(228) - ERROR - Unable to access https://rest-u056.immedia-semi.com/networks after token refresh.

Tombost commented 2 months ago

I have the "same" problem, my token is not valid after 24h

fronzbot commented 2 months ago

Hmmm well that's concerning. Probably a much larger API overhaul 😞

shred444 commented 2 months ago

same issue here, I'm getting this error:

Unable to access https://rest-u020.immedia-semi.com/api/v3/accounts/348690/homescreen after token refresh

dontcallmeshirley commented 2 months ago

Not sure if this helps or not, but blinkpy stopped working a few weeks ago for me. Tried the endpoint on this project and works plenty fine as of earlier today: https://github.com/nayrk/Blink Seems to use a combination of endpoints all of which are currently working~

fronzbot commented 2 months ago

@dontcallmeshirley those endpoints are three years old. Which ones did you use specifically? If those are working it's much more likely that Blink is identifying traffic from this library and blocking that rather than endpoints being down

Tombost commented 2 months ago

@fronzbot it seems that the token expires after 24h. So we need to ask new token, but in my case the API ask me every time the SMS pin. (even if I have the "reauth": true) I have reversed the blink android apk to check how the app can connect without asking the PIN every time. I have made some test and now I'm able to login again with a "trusted" client. (using nodeJS)

I login using :

{
    "app_version":"30.0",
    "client_name": "unraid-api",
    "client_type":"android",
    "device_identifier": "nodered/unraid",
    "email": "mail@tld.com",
    "os_version":"14",
    "password": "hellohello",
    "reauth": true,
    "unique_id":"myID_unique_random"
}

POST https://rest-e005.immedia-semi.com/api/v5/account/login The API respond with a lot of info, use the auth token and clientID :

msg.headers = { 'token-auth': msg.payload.auth.token }
msg.clientID = msg.payload.account.clientID;
msg.payload = 

{
    "pin": sms_pin, //enter the PIN you got by SMS
    "trusted":true
}
return msg;

POST https://rest-e005.immedia-semi.com/api/v5/accounts/xxxxxx/users/xxxxxx/clients/{{clientID}}/client_verification/pin/verify

And now I'm able to "login" using https://rest-e005.immedia-semi.com/api/v5/account/login without an SMS PIN, so i can get my new token every time.

I hope this works tomorrow lol

adrpibgal commented 2 months ago

same issue here, I'm getting this error:

Unable to access https://rest-u020.immedia-semi.com/api/v3/accounts/348690/homescreen after token refresh

Same for me. Just get an email with a link to update password, no PIN

adrpibgal commented 2 months ago

Not sure if this helps or not, but blinkpy stopped working a few weeks ago for me. Tried the endpoint on this project and works plenty fine as of earlier today: https://github.com/nayrk/Blink Seems to use a combination of endpoints all of which are currently working~

This script only works for my v1 cameras, not for minis

Tombost commented 2 months ago

@fronzbot it seems that the token expires after 24h. So we need to ask new token, but in my case the API ask me every time the SMS pin. (even if I have the "reauth": true) I have reversed the blink android apk to check how the app can connect without asking the PIN every time. I have made some test and now I'm able to login again with a "trusted" client. (using nodeJS)

I login using :

{
    "app_version":"30.0",
    "client_name": "unraid-api",
    "client_type":"android",
    "device_identifier": "nodered/unraid",
    "email": "mail@tld.com",
    "os_version":"14",
    "password": "hellohello",
    "reauth": true,
    "unique_id":"myID_unique_random"
}

POST https://rest-e005.immedia-semi.com/api/v5/account/login The API respond with a lot of info, use the auth token and clientID :

msg.headers = { 'token-auth': msg.payload.auth.token }
msg.clientID = msg.payload.account.clientID;
msg.payload = 

{
    "pin": sms_pin, //enter the PIN you got by SMS
    "trusted":true
}
return msg;

POST https://rest-e005.immedia-semi.com/api/v5/accounts/xxxxxx/users/xxxxxx/clients/{{clientID}}/client_verification/pin/verify

And now I'm able to "login" using https://rest-e005.immedia-semi.com/api/v5/account/login without an SMS PIN, so i can get my new token every time.

I hope this works tomorrow lol

Still working today

fronzbot commented 2 months ago

@Tombost - good stuff. I'm thinking this might just be user-agent shenanigans. We've had to make a change to the following "constants" before in order to get the library working again. The fact that you are successful will version 30 and we have it pegged to 27 makes me think updating that would work.

@mkmer - what do you think? Sound like a plausible root cause?

https://github.com/fronzbot/blinkpy/blob/cedb2d996da2ee000f7e7f2a1b027a6155f357c4/blinkpy/helpers/constants.py#L23-L24

mkmer commented 2 months ago

It does sound plausible. At the moment I'm not having the issue so I haven't been able to assist with the conversation.

shred444 commented 1 month ago

any update on this? is this as simple as modifying this constants file?

markeff commented 1 month ago

Similar to shred444, wondering if there is an update on this...and if changing the parameter in the constants file may help, what would be the value for the APP_BUILD field?

FYI, similar to above, after updating to the current blinkpy dev release, I get same issues as others:

Username:xxxxx@xxxxx.xxx /Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/getpass.py:91: GetPassWarning: Can not control echo on the terminal.   passwd = fallback_getpass(prompt, stream) Warning: Password input may be echoed. Password:[password]   Unable to access https://rest-e007.immedia-semi.com/api/v3/accounts/193463/homescreen after token refresh. Enter code sent to xxxxx@xxxxx.xxx: [123456] Did not receive valid response from server. Error: 'valid' Unable to access https://rest-e007.immedia-semi.com/networks after token refresh. Traceback (most recent call last):   File "/Users/xxxxx/PycharmProjects/blinkpy_v2/main.py", line 125, in     blink = asyncio.run(start())             ^^^^^^^^^^^^^^^^^^^^   File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/runners.py", line 190, in run     return runner.run(main)            ^^^^^^^^^^^^^^^^   File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/runners.py", line 118, in run     return self._loop.run_until_complete(task)            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^   File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete     return future.result()            ^^^^^^^^^^^^^^^   File "/Users/xxxxx/PycharmProjects/blinkpy_v2/main.py", line 63, in start     camera = blink.cameras['G8T1-XXXX-XXXX-XXXX']              ~~~~~^^^^^^^^^^^^^^^^^^^^^^^   File "/Users/xxxxx/PycharmProjects/blinkpy_v2/.venv/lib/python3.11/site-packages/requests/structures.py", line 52, in getitem     return self._store[key.lower()][1]            ~~~^^^^^^^^^^^^^ KeyError: 'g8t1-xxxx-xxxx-xxxx' Unclosed client session client_session: <aiohttp.client.ClientSession object at 0x105845210> Unclosed connector connections: ['[(<aiohttp.client_proto.ResponseHandler object at 0x11c1491d0>, 123598.968620959)]', '[(<aiohttp.client_proto.ResponseHandler object at 0x11c148f30>, 123599.275386575)]'] connector: <aiohttp.connector.TCPConnector object at 0x117adae50>

fronzbot commented 1 month ago

No update as neither myself nor @mkmer can recreate the problem so the best we can do is throw stuff at the wall to see if it sticks. Based on my best guess at the moment, updating the constants file might work, but I have no way to verify that. If someone with the issue wants to experiment and submit a PR if it works, I'm happy to merge that!

markeff commented 1 month ago

Have tried updating the constants file with a few permutation (example below) but no joy as yet...will keep trying. Just throws "Did not receive valid response from server. Error: 'valid'"

APP_BUILD = "ANDROID_28506099" DEFAULT_USER_AGENT = "30.0ANDROID_28506099"

Anomen commented 1 month ago

Hi everyone, I still experience the bug on my side. Going on vacations tomorrow but I was wondering: did anyone try to sniff the traffic from the phone to get the url to refresh the token? I can do it when I come back in two weeks but I was curious to see if anyone tried that.

Tombost commented 1 month ago

This is what I have already done. Every time the blink app is open, the app do a login to the API. I do the same with my Node-RED application and it’s working fine.

Screenshot_20240514_095347_PCAPdroid

The app got the new token, and use it after for every request

Anomen commented 1 month ago

@Tombost : thanks, super useful!

@fronzbot it seems that the token expires after 24h. So we need to ask new token, but in my case the API ask me every time the SMS pin. (even if I have the "reauth": true) I have reversed the blink android apk to check how the app can connect without asking the PIN every time. I have made some test and now I'm able to login again with a "trusted" client. (using nodeJS) I login using :

{
    "app_version":"30.0",
    "client_name": "unraid-api",
    "client_type":"android",
    "device_identifier": "nodered/unraid",
    "email": "mail@tld.com",
    "os_version":"14",
    "password": "hellohello",
    "reauth": true,
    "unique_id":"myID_unique_random"
}

POST https://rest-e005.immedia-semi.com/api/v5/account/login The API respond with a lot of info, use the auth token and clientID :

msg.headers = { 'token-auth': msg.payload.auth.token }
msg.clientID = msg.payload.account.clientID;
msg.payload = 

{
    "pin": sms_pin, //enter the PIN you got by SMS
    "trusted":true
}
return msg;

POST https://rest-e005.immedia-semi.com/api/v5/accounts/xxxxxx/users/xxxxxx/clients/{{clientID}}/client_verification/pin/verify And now I'm able to "login" using https://rest-e005.immedia-semi.com/api/v5/account/login without an SMS PIN, so i can get my new token every time. I hope this works tomorrow lol

Still working today

After some of my test, it looks like it may be the "trusted" parameter. Did it still work after a while?

Anomen commented 1 month ago

Ok, I think I found something. Is it possible that the issue comes from the fact that we generate different unique_id every time here? If I put a fixed value, it does not seem to ask me the PIN code when I run the app again.

Also, I noticed that if we call the client_verification/pin/verify endpoint with

{
    "pin": 12345,
    "trusted": true
}

the response is:

{
    "valid": true,
    "trusted": true,
    "require_new_pin": false,
    "allow_pin_resend_seconds": 90,
    "code": 1626,
    "message": "Client has been successfully verified"
}

Without the "trusted": true in the body, the response is "trusted": false. I'm not sure yet what it changes, but I could see some people saying it'd ask for the PIN again after 24 hours.. maybe it's that.

Tombost commented 1 month ago

In my case I use same unique ID everytime and I have added trusted:true And now my system work fine since 8-9 days

Anomen commented 1 month ago

Ok, awesome, so it looks like it's just that. I don't know if it's the way I coded my script that generates a new ID each time I run it (it's very much possible), or if it's how the lib is designed. I'll take a look when I come back from vacay.

fronzbot commented 1 month ago

@Anomen - it's only generated again if the data is not supplied as part of the login method. The intent is to login once save the credentials (uid, etc) and then load that file on subsequent calls. That's what the dict.get() method does- if the key is not found in the login, gen_uid() is called. Hope that makes sense!

DrBakterius commented 1 month ago

Hello!

I can't keep up with your discussion. It sounds too complicated for me. Is there a description of what I need to do to make it work again?

So far I have used the following script to save an image:

import requests

from blinkpy.blinkpy import Blink
from blinkpy.auth import Auth
from blinkpy.camera import BlinkCamera
blink = Blink()
auth = Auth({"username": "*****@***.**", "password": "*************"}, no_prompt=True)
blink.auth = auth
blink.start()
auth.send_auth_key(blink, "******")
blink.setup_post_verify()

bild = '/user/blink.jpg'

for name, camera in blink.cameras.items(): print(name), print(camera.attributes)

camera = blink.cameras['Door']
camera.snap_picture()
blink.refresh()
camera.image_to_file(bild)

Do I have to change anything? If so, what?

Thanks and best regards

markeff commented 1 month ago

When saving the creds.json file after a successful authentication (i.e. prompt route with username and password and then entering the 2FA code received via text) the creds file has userid: null. All other fields populated appropriately.

What does userid relate to and how is this generated?

markeff commented 1 month ago

It's been a very long time since I've done any coding so it is taking me a while to figure through this and I don't have an Android device to network sniff. It's still not resolved as I've not a different issue now relating to "Unable to retrieve cameras from response" but I seem to have got past the verify pin issue on the 2FA response.

In auth.py, at the point it tries to extract the login response, it wasn't pulling out the user_id to then insert into the verify pin call - i.e. in the xxxxxx below it was simply "None" and then fails with a 400 Bad Request. POST:https://rest-e007.immedia-semi.com/api/v5/accounts/xxxxxx/users/**xxxxxx**/clients/{{clientID}}/client_verification/pin/verify

It seems the user_id (for me at least) is one digit less than the account_id. Inserting an extra line in the extract_login_info appears to correct this i.e. addition of last line below

def extract_login_info(self): """Extract login info from login response.""" self.region_id = self.login_response["account"]["tier"] self.host = f"{self.region_id}.{BLINK_URL}" self.token = self.login_response["auth"]["token"] self.client_id = self.login_response["account"]["client_id"] self.account_id = self.login_response["account"]["account_id"] self.user_id = self.login_response["account"]["user_id"]

jrhunger commented 1 month ago

Looks like the "unable to retrieve cameras from the response" is due to setup_owls dereferencing homescreen without checking if it is null. Setting no_owls = True doesn't help, because homescreen is still null in that case. If you don't set that and aren't logged in, it looks like:

$ python3 login.py
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x7fdc0614f400>
Unable to access https://rest-u056.immedia-semi.com/api/v3/accounts/195415/homescreen after token refresh.

I'm not sure if get_homescreen needs to be called in setup_post_verify, or just needs to be checked for null at the link above.

markeff commented 1 month ago

@jrhunger I'm seeing the same thing. I'm not sure what the normal flow should be but get_homescreen fails first time due to the need for 2FA. It does not get recalled so homescreen is null and then fails on setup_owls. (I don't have any Blink Minis in my camera list).

I think there is still something may have changed/still not right in the client verify step as despite setting up the 2FA and my client appearing in the trusted client list on the app, it still prompts for a new 2FA next time I try to access via python. and returns 'client_trusted': False, 'client_verification_required': True, 'require_trust_client_device': True

jrhunger commented 1 month ago

By making your change above, and also this one in blinkpy.py, plus following the new async example (incidentally horrible timing for this amazon issue right after the async change which makes using/understanding the library much more confusing) in blinkapp.py/setup_camera_list, was at least able to get it to download my videos after typing in the 2FA. That's progress.

            #mini_cameras = await self.setup_owls()
            mini_cameras = []
            #lotus_cameras = await self.setup_lotus()
            lotus_cameras = []
Panda88CO commented 1 month ago

I am seeing the same exact issue with homescreen - it is throwing an UnauthorizedError

It seems get_homescreen is called before the pin is entered - if moved to after 2FA is called it works I moved get_homescreen() to first function call in setup_post_verify() and it seems to work now I also updated the USER_AGENT to one suggested above - not sure if it matters

This is in the latest build before moving to asyncio (0.19.2) - porting my app to asynio is a large job Is there a way to make changes to the release before asyncio ? It appears (a quick glance at code) that get_homescreen is called before the 2FA is entered in the latest release (part of start() function). I do not want to make this change as I am not running the release so no way to test it

markeff commented 1 month ago

With the addition of the extra line detailed above in extract_login_info(self), this problem is now fixed for me and working fine. In my case there were two issues:

  1. ...didn't read the manual! When I first setup my code many months ago, I didn't save credentials each time to a json file to use for the next login. In fairness, it didn't seem to cause me an issue and I only needed to perform 2FA once and it would go for months without a new 2FA challenge. For whatever reason, this seems to have changed and I've now needed to save and reuse the credentials (specifically the same uid) to prevent 2FA on every subsequent login. Obvious really but I hadn't read properly the original API descriptions so have got a better idea now of expected behaviour.

  2. the change to the pin_verify url on the blink side. This seemed to be partially fixed in 0.23.0b7 but still wasn't correctly populating the userid in the url. The addition of the line above has fixed this for me now. It took my a while to realise that the pin_verify wasn't correctly responding to the 2FA challenge as the next part of my code polls for cameras available and it seems blink will respond to this even if the 2FA challenge has not been properly authenticated...it only then fails when you try and grab an image and returns 'not authorised'.

Anyway, seems to be working for me for the past few days and with no further issues, 2FA requests or similar!

@fronzbot I've not put this into a pull request as probably easier for you just to review and confirm whether what I've said above is right

Panda88CO commented 3 weeks ago

Question I am getting 'valid': True, 'trusted': True, 'require_new_pin': False, ... during my 2FA check, but I still get 2FA key send next time I start my program.
I noticed that I get the following as a response to the login / token refresh 'client_trusted': False, 'new_account': False, 'tier': 'u033', 'region': 'ap', 'account_verification_required': False, 'phone_verification_required': False, 'client_verification_required': True, 'require_trust_client_device': True, Is this the issue - do I need to use differnt parameters to pass to the login - I curently have: "device_identifier": "ISY_PG3x", "client_name": "POLISY", "reauth": true, "app_version": "30.0", "client_type": "android", "os_version": "14" Any ideas? Unique_id remains constant from run to run etc

fronzbot commented 3 weeks ago

Released blinkpy==0.23.0b8. If people can give that a shot to see if it fixes issues I'll make an official release

glanzel commented 2 weeks ago

I can confirm that with blinkpy==0.23.0b8 and { "trusted": true } my pin gets no longer rejected. Thanks.