Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix cloudflare 403 with curl_cffi #205

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions arlo.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

from request import Request
from eventstream import EventStream

# Import all of the other stuff.
from six import string_types, text_type
from datetime import datetime
Expand Down Expand Up @@ -136,7 +136,9 @@ def Login(self, username, password):
"""
self.username = username
self.password = password
self.request = Request()

# start login with curl_cffi to address cloudflare client fingerprinting
self.request = Request(impersonate=True)

headers = {
'Access-Control-Request-Headers': 'content-type,source,x-user-device-id,x-user-device-name,x-user-device-type',
Expand All @@ -146,7 +148,7 @@ def Login(self, username, password):
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_1_2 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Mobile/15B202 NETGEAR/v1 (iOS Vuezone)',
}
self.request.options(f'https://{self.AUTH_URL}/api/auth', headers=headers)

headers = {
'DNT': '1',
'schemaVersion': '1',
Expand All @@ -171,6 +173,8 @@ def Login(self, username, password):
)
headers['Authorization'] = body['token']

# create a new Request object with a session that can be used with sseclient
self.request = Request(impersonate=False)
self.request.session.headers.update(headers)

self.user_id = body['userId']
Expand All @@ -180,7 +184,9 @@ def LoginMFA(self, username, password, google_credential_file):
self.username = username
self.password = password
self.google_credentials = pickle.load(open(google_credential_file, 'rb'))
self.request = Request()

# start login with curl_cffi to address cloudflare client fingerprinting
self.request = Request(impersonate=True)

# request MFA token
request_start_time = int(time.time())
Expand Down Expand Up @@ -285,6 +291,8 @@ def LoginMFA(self, username, password, google_credential_file):
'Authorization': finish_auth_body['data']['token'].encode('utf-8'),
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_1_2 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Mobile/15B202 NETGEAR/v1 (iOS Vuezone)',
}
# create a new Request object with a session that can be used with sseclient
self.request = Request(impersonate=False)
self.request.session.headers.update(headers)
self.BASE_URL = 'myapi.arlo.com'

Expand Down
18 changes: 15 additions & 3 deletions request.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import requests
from requests.exceptions import HTTPError
from curl_cffi import requests as curl_cffi_requests

#from requests_toolbelt.utils import dump
#def print_raw_http(response):
Expand All @@ -25,8 +26,15 @@
class Request(object):
"""HTTP helper class"""

def __init__(self):
self.session = requests.Session()
def __init__(self, impersonate=False):
# we create different sessions based on different technical needs:
# - curl_cffi is used to impersonate a browser and bypass client fingerprinting
# - requests is used to produce a feature-filled Session that can be compatible with sseclient
self.impersonate = impersonate
if impersonate:
self.session = curl_cffi_requests.Session(impersonate="chrome110")
else:
self.session = requests.Session()

def _request(self, url, method='GET', params={}, headers={}, stream=False, raw=False):

Expand All @@ -44,7 +52,11 @@ def _request(self, url, method='GET', params={}, headers={}, stream=False, raw=F

if method == 'GET':
#print('COOKIES: ', self.session.cookies.get_dict())
r = self.session.get(url, params=params, headers=headers, stream=stream)
if self.impersonate:
# curl_cffi does no suppot the stream keyword
r = self.session.get(url, params=params, headers=headers)
else:
r = self.session.get(url, params=params, headers=headers, stream=stream)
r.raise_for_status()
if stream is True:
return r
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def readme():
url='https://github.com/jeffreydwalter/arlo',
license='Apache Software License',
include_package_data=True,
install_requires=['monotonic', 'requests', 'responses==0.10.15', 'urllib3==1.24', 'sseclient==0.0.22', 'PySocks', 'pickle-mixin', 'google-api-python-client', 'google-auth-oauthlib'],
install_requires=['monotonic', 'requests', 'responses==0.10.15', 'urllib3==1.24', 'sseclient==0.0.22', 'PySocks', 'pickle-mixin', 'google-api-python-client', 'google-auth-oauthlib', 'curl-cffi==0.5.7'],
keywords=[
'arlo',
'camera',
Expand Down