-
Notifications
You must be signed in to change notification settings - Fork 17
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
Sign-in API call results in 403 - You are forbidden to perform this operation #51
Comments
@JeffResc |
Anyone formally reached out to Shark to complain? This is a violation of their product terms; We purchased it with access to the API, they can't legally revoke it. |
@fermulator Unfortunately they do not have any legal requirement to keep their API available. Not to mention, this particular integration was a reverse engineered API which simply isn't something they even support. Let's pretend for a moment, though, that they did want to support it. These companies write their TOS in such away that they can change them at any given time with very little notice. At least in the US, there's very little legislation around anything that relates to this. Unless there is some other way to access the API now, we might be out of luck here. |
It looks like they've shifted to using Auth0 inside the iOS app (I'm new to Shark, not sure if that's a recent development or if the Ayla API was just an easier way in). There's a backend flow starting from here that uses this mechanism to acquire tokens and some OAuth2 bits. I wasn't able to MITM a successful login from my phone, the final step in the flow returns an iOS Shark app location that doesn't appear to get intercepted by the proxy - whatever happens there, the flow seems to fail and revert to its beginning (I guess if it doesn't have the state associated with the start of the flow in that other, non-intercepted call, it can't succeed). The Auth0 documentation and code samples probably provide enough so that if one captured the Shark app client ID and had valid user credentials to use, they could plug the right values in to an imitation flow and get a token at the end. It might need to differ from a captured flow a bit by using a non-iOS |
The flow is straightforward, but Auth0 has an aggressive bot detection mechanism behind the scenes that injects(?) a captcha requirement I didn't see in my MITM logs. Or maybe it's just recently been added universally. If it could be run without triggering the captcha obstacle, this snippet would simulate iOS and should nominally work based on their documentation. I probably got flagged as an attacker while drawing it up, so I never got past the end of step 3 - the captcha value is incorrect, so it returns an HTTP 400 instead of a redirect to resume the flow. To run it, you need to extract the app ID, and you'll need to provide your own username and password. Remove the There are murky, unpublished behavioral rules behind the clear technical specs that make this an unlikely path. Maybe someone can take it farther - I suspect if you can pass the cookies and URL to a separate browser session - much like the mobile app login - you can then manage the captcha bit manually and get a token in the end.
import base64
import logging
import random
import requests
import time
from hashlib import sha256
from auth0.authentication import GetToken
def main():
import http.client
http.client.HTTPConnection.debuglevel = 5
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("urllib3").setLevel(logging.DEBUG)
shark_base = 'login.sharkninja.com'
shark_base_url = 'https://' + shark_base
client_id = 'extract-from-app'
redirect_uri = 'com.sharkninja.shark://' + shark_base + '/ios/com.sharkninja.shark/callback'
headers = {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0.1 Mobile/15E148 Safari/604.1',
'Accept-Language': 'en-US,en:q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
}
# Prep: Create verifier and code challenge
verifier = base64.urlsafe_b64encode(random.randbytes(32))
code_challenge = base64.urlsafe_b64encode(sha256(verifier).hexdigest())
state = sha256(base64.urlsafe_b64encode(random.randbytes(32))).hexdigest()
# Step 1: Begin auth flow - GET /authorize | Redirect to login
flow_session = requests.Session()
# auth0Client just describes the iOS in-app client in Base64 JSON
resp = flow_session.get(shark_base_url + '/authorize', params={
'response_type': 'code',
'code_challenge': code_challenge,
'code_challenge_method': 'S256',
'client_id': client_id,
'scope': 'openid profile email offline_access read:users read:current_user read:user_idp_tokens',
'screen_hint': 'signin',
'ui_locales': 'en',
'auth0Client': 'eyJ2ZXJzaW9uIjoiMi42LjAiLCJlbnYiOnsic3dpZnQiOiI1LngiLCJpT1MiOiIxOC4wIn0sIm5hbWUiOiJBdXRoMC5zd2lmdCJ9',
'state': state,
'redirect_uri': redirect_uri,
}, headers=headers, allow_redirects=False)
login_url = resp.headers['Location']
# Step 2: Begin user login - GET /u/login | Nothing, for appearances sake
login_state = login_url.split('=')[1].split('&')[0]
resp = flow_session.get(shark_base_url + login_url, headers=headers)
time.sleep(random.randrange(20,26))
# Step 3: Login user - POST /u/login | Redirect to resume flow
add_referer = { 'Referer': shark_base_url + login_url }
resp = flow_session.post(shark_base_url + login_url, data={
'state': login_state,
'username': 'your-username',
'password': 'your-password',
'captcha': '',
'action': 'default',
}, headers={**headers, **add_referer}, allow_redirects=False)
auth_url = resp.headers['Location']
# Step 4: Get auth token - GET /authorize | Return auth token
resp = get(auth_url, headers=headers, allow_redirects=False)
callback_url = resp.headers['Location']
auth_code = callback_url.split('=')[1]
# Step 5: Exchange for access token | Return access token
gt = GetToken(shark_base, client_id)
token = gt.authorization_code_pkce(
code_verifier=verifier,
code=auth_code,
redirect_uri=redirect_uri,
grant_type='authorization_code'
)
print(token)
if __name__ == '__main__':
main() |
I gave the above a go, but it just fails on the final exchange of auth code for a token. Running a MITM myself the values looks correct but I always receive a response with the body
|
@andyjhall That's farther than I got - so you didn't encounter a captcha? I've noticed in the Shark app it now always shows me a captcha section when I need to log in. Looking up the error prompted me to review how I was generating the verifier and code_challenge - I missed that the challenge needed to also be Base64 encoded. I've updated that line, could you try again when you have a chance? |
POST request to
/users/sign_in.json
is now resulting in a 403 with the error "You are forbidden to perform this operation" with no reason as to why this API call is no longer functioning as it originally was.I have confirmed that the
app_id
andapp_secret
constants seem to still be correct as changing these values will instead result in a 404 as expected.Originally reported at home-assistant/core#123392
The text was updated successfully, but these errors were encountered: