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

fixed login errors #70

Closed
wants to merge 2 commits into from
Closed
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
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def read_from_file(key):
],
install_requires=[
'beautifulsoup4',
'httpx>=0.18,<0.21',
'httpx==0.20.*',
'pbkdf2',
'Pillow',
'pyaes',
Expand Down
80 changes: 45 additions & 35 deletions src/audible/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,19 @@ def get_soup(resp, log_errors=True):
return soup


def get_inputs_from_soup(
def get_form_data(
soup: BeautifulSoup,
default_url: httpx.URL,
search_field: Optional[Dict[str, str]] = None) -> Dict[str, str]:
"""Extracts hidden form input fields from a Amazon login page."""
"""Extracts hidden form input fields, next request method and url from
a Amazon login page."""

if search_field is None:
search_field = {"name": "signIn"}

search_field = search_field or {"name": "signIn"}
form = soup.find("form", search_field) or soup.find("form")
method = form.get("method", "GET")
url = form.get("action", default_url)
inputs = {}
for field in form.find_all("input"):
try:
Expand All @@ -116,17 +122,7 @@ def get_inputs_from_soup(
inputs[field["name"]] = field["value"]
except BaseException:
pass
return inputs


def get_next_action_from_soup(
soup: BeautifulSoup, search_field: Optional[Dict[str, str]] = None
) -> Tuple[str, str]:
form = soup.find("form", search_field) or soup.find("form")
method = form["method"]
url = form["action"]

return method, url
return method, url, inputs


def create_code_verifier(length: int = 32) -> bytes:
Expand Down Expand Up @@ -341,7 +337,8 @@ def login(
session = httpx.Client(
base_url=base_url,
headers=default_headers,
cookies=init_cookies
cookies=init_cookies,
follow_redirects=True
)
code_verifier = create_code_verifier()

Expand All @@ -357,15 +354,17 @@ def login(
oauth_resp = session.get(oauth_url)
oauth_soup = get_soup(oauth_resp)

login_inputs = get_inputs_from_soup(oauth_soup)
method, url, login_inputs = get_form_data(
soup=oauth_soup,
default_url=oauth_resp.url,
search_field={"name": "signIn"}
)
login_inputs["email"] = username
login_inputs["password"] = password

metadata = meta_audible_app(USER_AGENT, base_url)
login_inputs["metadata1"] = encrypt_metadata(metadata)

method, url = get_next_action_from_soup(oauth_soup, {"name": "signIn"})

login_resp = session.request(method, url, data=login_inputs)
login_soup = get_soup(login_resp)

Expand All @@ -377,23 +376,27 @@ def login(
else:
guess = default_captcha_callback(captcha_url)

inputs = get_inputs_from_soup(login_soup)
method, url, inputs = get_form_data(
soup=login_soup,
default_url=login_resp.url
)
inputs["guess"] = guess
inputs["use_image_captcha"] = "true"
inputs["use_audio_captcha"] = "false"
inputs["showPasswordChecked"] = "false"
inputs["email"] = username
inputs["password"] = password

method, url = get_next_action_from_soup(login_soup)

login_resp = session.request(method, url, data=inputs)
login_soup = get_soup(login_resp)

# check for choice mfa
# https://www.amazon.de/ap/mfa/new-otp
while check_for_choice_mfa(login_soup):
inputs = get_inputs_from_soup(login_soup)
method, url, inputs = get_form_data(
soup=login_soup,
default_url=login_resp.url
)
for node in login_soup.select(
"div[data-a-input-name=otpDeviceContext]"
):
Expand All @@ -402,8 +405,6 @@ def login(
inp_node = node.find("input")
inputs[inp_node["name"]] = inp_node["value"]

method, url = get_next_action_from_soup(login_soup)

login_resp = session.request(method, url, data=inputs)
login_soup = get_soup(login_resp)

Expand All @@ -414,13 +415,14 @@ def login(
else:
otp_code = default_otp_callback()

inputs = get_inputs_from_soup(login_soup)
method, url, inputs = get_form_data(
soup=login_soup,
default_url=login_resp.url
)
inputs["otpCode"] = otp_code
inputs["mfaSubmit"] = "Submit"
inputs["rememberDevice"] = "false"

method, url = get_next_action_from_soup(login_soup)

login_resp = session.request(method, url, data=inputs)
login_soup = get_soup(login_resp)

Expand All @@ -431,19 +433,21 @@ def login(
else:
cvf_code = default_cvf_callback()

inputs = get_inputs_from_soup(login_soup)

method, url = get_next_action_from_soup(login_soup)
method, url, inputs = get_form_data(
soup=login_soup,
default_url=login_resp.url
)

login_resp = session.request(method, url, data=inputs)
login_soup = get_soup(login_resp)

inputs = get_inputs_from_soup(login_soup)
method, url, inputs = get_form_data(
soup=login_soup,
default_url=login_resp.url
)
inputs["action"] = "code"
inputs["code"] = cvf_code

method, url = get_next_action_from_soup(login_soup)

login_resp = session.request(method, url, data=inputs)
login_soup = get_soup(login_resp)

Expand All @@ -454,11 +458,18 @@ def login(
else:
default_approval_alert_callback()

url = login_soup.find(id="resend-approval-link")["href"]
url = login_resp.url

login_resp = session.get(url)
login_soup = get_soup(login_resp)

while login_soup.find(
"span", {"class": "transaction-approval-word-break"}
): # a-size-base-plus transaction-approval-word-break a-text-bold
login_resp = session.get(url)
login_soup = get_soup(login_resp)
print("still waiting for redirect")

session.close()

if b"openid.oa2.authorization_code" not in login_resp.url.query:
Expand Down Expand Up @@ -533,4 +544,3 @@ def external_login(
"domain": domain,
"serial": serial
}