arlo icon indicating copy to clipboard operation
arlo copied to clipboard

403 Client Error: Forbidden for url: https://ocapi-app.arlo.com/api/auth

Open scuc opened this issue 1 year ago • 19 comments

Is anyone else seeing this 403 Client Error? I started to see it on May 09, my script runs automated so I didn't actually notice this issue in my logs until last week.
I thought maybe it was an MFA issue, so I've tried to add MFA as explained in the wiki, but I cant even get past the 403 error.

What version of Python are you using (python -V)?

Python 3.10.4

What operating system and processor architecture are you using (python -c 'import platform; print(platform.uname());')?

('Darwin', 'MacPro-Mojave.local', '18.7.0', 'Darwin Kernel Version 18.7.0: Tue Jun 22 19:37:08 PDT 2021; root:xnu-4903.278.70~1/RELEASE_X86_64', 'x86_64', 'i386')

Which Python packages do you have installed (run the pip freeze or pip3 freeze command and paste output)?

arlo==1.2.59 (also test  with .64 version)
cachetools==5.3.1
certifi==2023.5.7
cffi==1.15.0
charset-normalizer==3.1.0
click==8.1.3
cloudscraper==1.2.60
cryptography==37.0.2
google-api-core==2.11.1
google-api-python-client==2.90.0
google-auth==2.21.0
google-auth-httplib2==0.1.0
google-auth-oauthlib==1.0.0
googleapis-common-protos==1.59.1
httplib2==0.22.0
idna==3.4
monotonic==1.6
oauthlib==3.2.2
paho-mqtt==1.6.1
pickle-mixin==1.0.2
protobuf==4.23.3
pyaarlo @ git+https://github.com/twrecked/pyaarlo@2d6941dc903fe2fca01ac9d82fe708d1299ebe81
pyasn1==0.5.0
pyasn1-modules==0.3.0
pycparser==2.21
pycryptodome==3.14.1
pyparsing==3.1.0
PySocks==1.7.1
PyYAML==6.0
requests==2.31.0
requests-oauthlib==1.3.1
requests-toolbelt==0.9.1
rsa==4.9
six==1.16.0
sseclient==0.0.22
Unidecode==1.3.4
uritemplate==4.1.1
urllib3==1.24

Which version of ffmpeg are you using (ffmpeg -version)?

ffmpeg version 1.2-tessus Copyright (c) 2000-2013 the FFmpeg developers
  built on Mar 15 2013 01:18:55 with llvm-gcc 4.2.1 (LLVM build 2336.1.00)
  configuration: --prefix=/Users/tessus/data/ext/ffmpeg/sw --as=yasm --extra-version=tessus --disable-shared --enable-static --disable-ffplay --enable-gpl --enable-pthreads --enable-postproc --enable-libmp3lame --enable-libtheora --enable-libvorbis --enable-libx264 --enable-libxvid --enable-libspeex --enable-bzlib --enable-zlib --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libxavs --enable-version3 --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-libvpx --enable-libgsm --enable-libopus --enable-fontconfig --enable-libfreetype --enable-libass --enable-filters --enable-runtime-cpudetect
  libavutil      52. 18.100 / 52. 18.100
  libavcodec     54. 92.100 / 54. 92.100
  libavformat    54. 63.104 / 54. 63.104
  libavdevice    54.  3.103 / 54.  3.103
  libavfilter     3. 42.103 /  3. 42.103
  libswscale      2.  2.100 /  2.  2.100
  libswresample   0. 17.102 /  0. 17.102
  libpostproc    52.  2.100 / 52.  2.100

Which Arlo hardware do you have (camera types - [Arlo, Pro, Q, etc.], basestation model, etc.)?

not relevant

What did you do?

This code was working for me up without MFA until May 09 2023 - I thought maybe it was an MFA issue, so I added MFA to the code, but still getting the same errrors.

from arlo import Arlo
from datetime import timedelta, date, datetime
import logging
# from pathlib import Path

import config
import check_subdirs as chksub

config = config.get_config()

USERNAME = config['creds']['username']
PASSWORD = config['creds']['password']
MFA = config['creds']['mfa']

logger = logging.getLogger(__name__)

ROOTPATH = config['paths']['root_path']

def download_mp4s(): 
    """
    Iterate over the videos in the arlo S3 bucket, compare against videos stored locally, dowload 
    all new files, rename downloaded files with a datetime, skip those that have already been downloaded. 
    """
    # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached.
    # Subsequent successful calls to login will update the oAuth token.
    try: 
        arlo = Arlo(USERNAME, PASSWORD, MFA)
        logger.info("Successfully logged into Arlo account")

        today = (date.today()-timedelta(days=0)).strftime("%Y%m%d")
        start_date = (date.today()-timedelta(days=30)).strftime("%Y%m%d")

        # Get all of the recordings for a date range.
        library = arlo.GetLibrary(start_date, today)
        logger.info("Connected to the Arlo Library")
        devices = arlo.GetDevices()
        # print(devices)

        device_list = []

        for device in devices:
            deviceId = device["deviceId"]
            deviceName = device["deviceName"]
            device_dict = {device["deviceId"]: device["deviceName"]}
            device_list.append(device_dict)

        logger.info(f"Devices found in library: {device_list}")

        # Iterate through the recordings in the library.
        download_count = 0
        for recording in library:

            deviceId = recording["deviceId"]

            deviceNum = next(i for i,d in enumerate(device_list) if deviceId in d)

            deviceName = device_list[deviceNum][deviceId]

            videofilename = datetime.fromtimestamp(int(recording['name'])//1000).strftime('%Y-%m-%d_%H-%M-%S') + '_' + recording['uniqueId'] + '.mp4'

            ###################
            # The videos produced by Arlo are pretty small, even in their longest, best quality settings,
            # but you should probably prefer the chunked stream (see below).
            ##################
            #    # Download the whole video into memory as a single chunk.
            #    video = arlo.GetRecording(recording['presignedContentUrl'])
            #	 with open('videos/'+videofilename, 'wb') as f:
            #        f.write(video)
            #        f.close()
            ################33

            root_dir = (ROOTPATH + deviceName + '/')
            mp4_path = chksub.check_subdirs(root_dir, deviceName, videofilename)

            if mp4_path.is_file() is not True:
                stream = arlo.StreamRecording(recording['presignedContentUrl'])
                with open(str(mp4_path), 'wb') as f:
                    for chunk in stream: 
                        f.write(chunk)
                    f.close()
                    download_msg = 'Downloaded '+ videofilename+' from '+ recording['createdDate']+'.'
                    dowload_path_msg = "Download path: " + str(mp4_path)
                    download_count += 1
                    logger.info(download_msg)
                    logger.info(dowload_path_msg)
            else:
                # Get video as a chunked stream; this function returns a generator.
                skip_msg = "Skipping: " + str(videofilename)
                logger.debug(skip_msg)
                continue

        # Delete all of the videos you just downloaded from the Arlo library.
        # Notice that you can pass the "library" object we got back from the GetLibrary() call.
        # result = arlo.BatchDeleteRecordings(library)

        if download_count != 0: 
            logger.info(f"Total videos downloaded: {download_count}")
        else: 
            logger.info(f"No new videos for download.")
        return

    except Exception as e:
        logger.exception(e)
      

if __name__ == '__main__':
    download_mp4s()

What did you expect to see?

I use the code listed above to download a local copy of my arlo videos.

What did you see instead?

    ================================================================
                ARLO Download Script - Start
                    Wednesday, 28. June 2023 09:46PM
    ================================================================
    
2023-06-28 21:46:34,815 | ERROR | Function: download_mp4s() | Line 103 | 403 Client Error: Forbidden for url: https://ocapi-app.arlo.com/api/auth
Traceback (most recent call last):
  File "/Users/myname/_Github/ARLO-S3-Download/arlo_download.py", line 27, in download_mp4s
    arlo = Arlo(USERNAME, PASSWORD, MFA)
  File "/Users/myname/_Github/ARLO-S3-Download/.venv/lib/python3.10/site-packages/arlo.py", line 69, in __init__
    self.LoginMFA(username, password, google_credential_file)
  File "/Users/myname/_Github/ARLO-S3-Download/.venv/lib/python3.10/site-packages/arlo.py", line 201, in LoginMFA
    auth_body = self.request.post(
  File "/Users/myname/_Github/ARLO-S3-Download/.venv/lib/python3.10/site-packages/request.py", line 80, in post
    return self._request(url, 'POST', params=params, headers=headers, raw=raw)
  File "/Users/myname/_Github/ARLO-S3-Download/.venv/lib/python3.10/site-packages/request.py", line 56, in _request
    r.raise_for_status()
  File "/Users/myname/_Github/ARLO-S3-Download/.venv/lib/python3.10/site-packages/requests/models.py", line 1021, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://ocapi-app.arlo.com/api/auth
2023-06-28 21:46:34,820 | INFO | Function: main() | Line 89 | 
        ================================================================
                    ARLO Download - Complete
                        Wednesday, 28. June 2023 09:46PM
        ================================================================

Does this issue reproduce with the latest release?

yes. same results. in .59 and .64, ive tried connecting through a VPN to see if I my IP was being blocked, i can also confirm that I have tried multiple user accounts - in each case I can log in just fine through the arlo web portal, but get the 403 when using the Arlo code.

scuc avatar Jun 29 '23 02:06 scuc