Skip to content

Commit

Permalink
feat: fix change password flow
Browse files Browse the repository at this point in the history
  • Loading branch information
pradishb committed Sep 17, 2024
1 parent 5f1bbba commit 526a179
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 58 deletions.
31 changes: 21 additions & 10 deletions league_client/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
}
113 changes: 74 additions & 39 deletions league_client/rso/auth.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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":
Expand All @@ -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"]
Expand Down
20 changes: 11 additions & 9 deletions league_client/rso/password.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -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,
Expand Down

0 comments on commit 526a179

Please sign in to comment.