diff --git a/league_client/constants.py b/league_client/constants.py index 8cf6925..f4d6a64 100644 --- a/league_client/constants.py +++ b/league_client/constants.py @@ -83,8 +83,11 @@ "acr_values": "", "claims": "", "client_id": "riot-client", - "code_challenge": "", - "code_challenge_method": "", + # commented because it returns 400 code + # when using website authorize flow + # + # "code_challenge": "", + # "code_challenge_method": "", "nonce": "SYXugqaAL5z7U7iioaTW5Q", "redirect_uri": "http://localhost/redirect", "response_type": "token id_token", @@ -95,8 +98,11 @@ "acr_values": "", "claims": "", "client_id": "lol", - "code_challenge": "", - "code_challenge_method": "", + # commented because it returns 400 code + # when using website authorize flow + # + # "code_challenge": "", + # "code_challenge_method": "", "nonce": "SYXugqaAL5z7U7iioaTW5Q", "redirect_uri": "http://localhost/redirect", "response_type": "token id_token", @@ -122,9 +128,14 @@ "response_type": "code", } -# LCU -SITE_URL = "https://authenticate.riotgames.com/api/v1/login" -USER_AGENT = ( - "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36" - " (KHTML, like Gecko) RiotClient/70.0.0 (CEF 74) Safari/537.36" -) + +PROD_XSS0_RIOTGAMES = { + "client_id": "prod-xsso-riotgames", + "scope": "openid account email offline_access", + "code_challenge_method": "S256", + "code_challenge": "zUo5VZKlO9BvmC_KX5sihHLmeR7iy7sRoHO2WYxON58", + "redirect_uri": "https://xsso.riotgames.com/redirect", + "state": "9ef38e6acae290380703408405", + "response_type": "code", + "prompt": "login", +} diff --git a/league_client/rso/auth.py b/league_client/rso/auth.py index 73fd983..ee8ca2d 100644 --- a/league_client/rso/auth.py +++ b/league_client/rso/auth.py @@ -1,4 +1,5 @@ from typing import Callable +from typing import Literal from typing import Optional from urllib.parse import parse_qs from urllib.parse import urlparse @@ -174,62 +175,91 @@ def authorize( password: str, captcha_solver: Callable[[str, str], str], params: dict[str, str] = RIOT_CLIENT_AUTH_PARAMS, + type: Literal["auth", "re-auth"] = "auth", ): - url = "https://auth.riotgames.com/api/v1/authorization" - res = client.post(url, json=params, headers=HEADERS) + # the authorization flow is different for + # website (prod-xsso-riotgames, accountodactyl-prod) + # and client (riot-client, lol) + # + # this flow is based on website but works + # for (riot-client, lol) + # + # if this flow stops working for (riot-client, lol) + # revert to commit 5f1bbba + + # step 1: set authorization params + url = "https://auth.riotgames.com/authorize" + res = client.get( + url, params=params, headers=HEADERS, follow_redirects=True + ) res.raise_for_status() + # step 2: get hcaptcha details (sitekey and rqdata) url = "https://authenticate.riotgames.com/api/v1/login" - body = { - "clientId": "riot-client", - "language": "en_US", - "platform": "windows", - "remember": True, - "riot_identity": {"state": "auth"}, - "type": "auth", - } - res = client.post(url, json=body, headers=HEADERS) + res = client.get(url, headers=HEADERS) res.raise_for_status() data = res.json() site_key = data["captcha"]["hcaptcha"]["key"] site_data = data["captcha"]["hcaptcha"]["data"] + # step 3: solve captcha token = captcha_solver(site_data, site_key) + # step 4: put authentication data (username/password) + # the response contains login token url = "https://authenticate.riotgames.com/api/v1/login" - body = { - "language": "en_US", - "remember": True, - "riot_identity": { - "captcha": f"hcaptcha {token}", - "password": password, - "username": username, - }, - "type": "auth", - } + if type == "auth": + body = { + "language": "en_US", + "remember": True, + "riot_identity": { + "captcha": f"hcaptcha {token}", + "password": password, + "username": username, + }, + "type": "auth", + } + else: + body = { + "type": "re-auth", + "remember": True, + "language": "en_US", + "riot_identity": { + "password": password, + "captcha": f"hcaptcha {token}", + }, + } res = client.put(url, json=body, headers=HEADERS) res.raise_for_status() + # step 5: post login token/redirect to complete authentication data = res.json() response_type = data["type"] if response_type == "success": - body = { - "authentication_type": "RiotAuth", - "code_verifier": "", - "login_token": data["success"]["login_token"], - "persist_login": True, - } - client.post( - "https://auth.riotgames.com/api/v1/login-token", - json=body, - headers=HEADERS, - ) - res.raise_for_status() - return client.post( - "https://auth.riotgames.com/api/v1/authorization", - params=params, - headers=HEADERS, - ) + # riot-client and lol redirects to localhost + # so, we use a different method to post login token + # for these clients + if params["client_id"] in ["riot-client", "lol"]: + body = { + "authentication_type": "RiotAuth", + "code_verifier": "", + "login_token": data["success"]["login_token"], + "persist_login": True, + } + res = client.post( + "https://auth.riotgames.com/api/v1/login-token", + json=body, + headers=HEADERS, + ) + res.raise_for_status() + else: + # NOTE: not raising for status because it can raise false + # positive when success + client.get( + data["success"]["redirect_url"], + headers=HEADERS, + follow_redirects=True, + ) elif response_type == "multifactor": raise AuthMultifactorError(res.text, res.status_code) elif response_type == "auth" and data["error"] == "auth_failure": @@ -248,13 +278,18 @@ def login_using_credentials( proxy: Optional[ProxyTypes] = None, ) -> tuple[str, str, str, str, str, str, str, str, str]: with httpx.Client(verify=SSL_CONTEXT, proxy=proxy) as client: - res = authorize( + authorize( client, username, password, captcha_solver, params, ) + res = client.post( + "https://auth.riotgames.com/api/v1/authorization", + params=params, + headers=HEADERS, + ) data = res.json() ssid = client.cookies["ssid"] clid = client.cookies["clid"] diff --git a/league_client/rso/password.py b/league_client/rso/password.py index e8f0b2e..cfefce8 100644 --- a/league_client/rso/password.py +++ b/league_client/rso/password.py @@ -7,8 +7,8 @@ from league_client.constants import ACCOUNTODACTYL_PARAMS from league_client.constants import HEADERS +from league_client.constants import PROD_XSS0_RIOTGAMES from league_client.constants import SSL_CONTEXT -from league_client.exceptions import AuthFailureError from league_client.rso.auth import authorize @@ -28,15 +28,17 @@ def change_password_using_credentials( proxy: Optional[ProxyTypes] = None, ) -> bool: with httpx.Client(verify=SSL_CONTEXT, proxy=proxy) as client: - res = authorize( - client, username, password, captcha_solver, ACCOUNTODACTYL_PARAMS + authorize( + client, username, password, captcha_solver, PROD_XSS0_RIOTGAMES + ) + authorize( + client, + username, + password, + captcha_solver, + ACCOUNTODACTYL_PARAMS, + "re-auth", ) - data = res.json() - if data["type"] != "response": - raise AuthFailureError("Failed to authorize") - url = data["response"]["parameters"]["uri"] - # redirect - client.get(url, headers=HEADERS, follow_redirects=True) res = client.get( "https://account.riotgames.com/", headers=HEADERS,