Skip to content

Commit

Permalink
feat!: add captcha support (#52)
Browse files Browse the repository at this point in the history
* feat: add captcha to auth

* docs: add captcha solver to readme

* bump: version to 2.1.0
  • Loading branch information
pradishb authored Sep 16, 2024
1 parent 4408c0d commit a4690f0
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 577 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,5 @@ cython_debug/

# Project
.vscode/
temp/
test.py
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,30 +46,42 @@ params = RIOT_CLIENT_AUTH_PARAMS
# league client data like rank, match history, missions, loot, etc
params = LEAGUE_CLIENT_AUTH_PARAMS

# captcha_solver is a function that takes rqdata and sitekey as arguments
# and should return a token, WebServerSolver from ValLib works but most of
# the captcha solving services do not work, feel free to contribute if you
# have a solution

# login using credentials
(
ssid,
clid,
access_token,
scope,
iss,
id_token,
token_type,
session_state,
expires_in,
) = login_using_credentials("USERNAME", "PASSWORD", params)

) = login_using_credentials(
username,
password,
captcha_solver,
params=RIOT_CLIENT_AUTH_PARAMS,
proxy=proxy,
)
# or login using session id, if you have already logged in once
# recommended to avoid rate limit
(
ssid,
clid,
access_token,
scope,
iss,
id_token,
token_type,
session_state,
expires_in,
) = login_using_ssid(ssid, params)
) = login_using_ssid(ssid, clid, params)


# puuid, region and account_id can be parsed from access_token
Expand Down Expand Up @@ -438,5 +450,5 @@ flash_key = get_flash_key(match_data, summoner_id)
```
from shortcuts.rso import get_account_data
account_data = get_account_data(username, password, proxy)
account_data = get_account_data(username, password, captcha_solver, proxy)
```
114 changes: 79 additions & 35 deletions league_client/rso/auth.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from typing import Callable
from typing import Optional
from urllib.parse import parse_qs
from urllib.parse import urlparse
Expand Down Expand Up @@ -144,12 +145,14 @@ def get_summoner_token(

def login_using_ssid(
ssid: str,
clid: str,
auth_params: dict[str, str] = RIOT_CLIENT_AUTH_PARAMS,
proxy: Optional[ProxyTypes] = None,
) -> tuple[str, str, str, str, str, str, str, str]:
) -> tuple[str, str, str, str, str, str, str, str, str]:
with httpx.Client(verify=SSL_CONTEXT, proxy=proxy) as client:
if ssid:
client.cookies.set("ssid", ssid, domain="auth.riotgames.com")
client.cookies.set("clid", clid, domain="auth.riotgames.com")
res = client.post(
"https://auth.riotgames.com/api/v1/authorization",
params=auth_params,
Expand All @@ -161,59 +164,100 @@ def login_using_ssid(
ssid = client.cookies["ssid"]
redirect_url = data["response"]["parameters"]["uri"]
data = process_redirect_url(redirect_url)
return (ssid, *data)
return (ssid, clid, *data)
raise InvalidSessionError(res.text, res.status_code)


def authorize(
client: httpx.Client,
username: str,
password: str,
captcha_solver: Callable[[str, str], str],
params: dict[str, str] = RIOT_CLIENT_AUTH_PARAMS,
) -> httpx.Response:
res = client.post(
"https://auth.riotgames.com/api/v1/authorization",
params=params,
headers=HEADERS,
)
):
url = "https://auth.riotgames.com/api/v1/authorization"
res = client.post(url, json=params, headers=HEADERS)
res.raise_for_status()
data = {

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",
"username": username,
"password": password,
}
res = client.post(url, json=body, headers=HEADERS)
res.raise_for_status()
data = res.json()
site_key = data["captcha"]["hcaptcha"]["key"]
site_data = data["captcha"]["hcaptcha"]["data"]

token = captcha_solver(site_data, site_key)

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",
}
# referer is very important to prevent cloudflare 403
headers = HEADERS.copy()
headers["referer"] = "https://riotgames.com/"
res = client.put(
"https://auth.riotgames.com/api/v1/authorization",
json=data,
headers=headers,
)
res = client.put(url, json=body, headers=HEADERS)
res.raise_for_status()
return res

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,
)
elif response_type == "multifactor":
raise AuthMultifactorError(res.text, res.status_code)
elif response_type == "auth" and data["error"] == "auth_failure":
raise AuthFailureError(res.text, res.status_code)
elif response_type == "auth" and data["error"] == "rate_limited":
raise RateLimitedError(res.text, res.status_code)
else:
raise AuthFailureError(res.text, res.status_code)


def login_using_credentials(
username: str,
password: str,
captcha_solver: Callable[[str, str], str],
params: dict[str, str] = RIOT_CLIENT_AUTH_PARAMS,
proxy: Optional[ProxyTypes] = None,
) -> tuple[str, str, str, str, str, str, str, str]:
) -> tuple[str, str, str, str, str, str, str, str, str]:
with httpx.Client(verify=SSL_CONTEXT, proxy=proxy) as client:
res = authorize(client, username, password, params)
res = authorize(
client,
username,
password,
captcha_solver,
params,
)
data = res.json()
response_type = data["type"]
if response_type == "response":
ssid = client.cookies["ssid"]
redirect_url = data["response"]["parameters"]["uri"]
data = process_redirect_url(redirect_url)
return (ssid, *data)
elif response_type == "multifactor":
raise AuthMultifactorError(res.text, res.status_code)
elif response_type == "auth" and data["error"] == "auth_failure":
raise AuthFailureError(res.text, res.status_code)
elif response_type == "auth" and data["error"] == "rate_limited":
raise RateLimitedError(res.text, res.status_code)
raise AuthFailureError(res.text, res.status_code)
ssid = client.cookies["ssid"]
clid = client.cookies["clid"]
redirect_url = data["response"]["parameters"]["uri"]
data = process_redirect_url(redirect_url)
return (ssid, clid, *data)
6 changes: 5 additions & 1 deletion league_client/rso/password.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import re
from typing import Callable
from typing import Optional

import httpx
Expand All @@ -23,10 +24,13 @@ def change_password_using_credentials(
username: str,
password: str,
new_password: str,
captcha_solver: Callable[[str, str], str],
proxy: Optional[ProxyTypes] = None,
) -> bool:
with httpx.Client(verify=SSL_CONTEXT, proxy=proxy) as client:
res = authorize(client, username, password, ACCOUNTODACTYL_PARAMS)
res = authorize(
client, username, password, captcha_solver, ACCOUNTODACTYL_PARAMS
)
data = res.json()
if data["type"] != "response":
raise AuthFailureError("Failed to authorize")
Expand Down
Loading

0 comments on commit a4690f0

Please sign in to comment.