Skip to content
Merged
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
70 changes: 59 additions & 11 deletions aiograpi/mixins/bloks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import re
import time
from asyncio import CancelledError
from http.cookies import SimpleCookie
from json import JSONDecodeError
from typing import Any, Dict, List, Optional
Expand Down Expand Up @@ -611,16 +612,34 @@ async def bloks_caa_login_oauth_token_fetch(
login=True,
)

async def bloks_caa_login_prepare(self, username: str = "", domain: Optional[str] = None) -> bool:
async def bloks_caa_login_prepare(
self,
username: str = "",
domain: Optional[str] = None,
waterfall_id: str = "",
offline_experiment_group: str = "caa_iteration_v3_perf_ig_4",
bloks_versioning_id: str = "",
) -> bool:
"""Run the ordered device and CAA preflight needed before login."""
if not self.usdid_registered and not (await self.usdid_register()):
return False
await self.bloks_caa_login_process_client_data(domain=domain)
await self.bloks_caa_login_process_client_data(
waterfall_id=waterfall_id,
offline_experiment_group=offline_experiment_group,
bloks_versioning_id=bloks_versioning_id,
domain=domain,
)
self.attestation_challenge_nonce = ""
self.attestation_key_nonce = ""
await self.attestation_create_android_keystore(domain=domain)
if self.caa_aac:
await self.bloks_caa_login_oauth_token_fetch(username=username, domain=domain)
await self.bloks_caa_login_oauth_token_fetch(
username=username,
waterfall_id=waterfall_id,
offline_experiment_group=offline_experiment_group,
bloks_versioning_id=bloks_versioning_id,
domain=domain,
)
return bool(self.caa_aac and self.attestation_challenge_nonce)

async def bloks_caa_login_send_request(
Expand All @@ -633,30 +652,54 @@ async def bloks_caa_login_send_request(
offline_experiment_group: str = "caa_iteration_v3_perf_ig_4",
bloks_versioning_id: str = "",
domain: Optional[str] = None,
auto_prepare: bool = True,
) -> Dict:
"""
Send the current CAA/Bloks login request used before Bloks 2FA.

This low-level helper requires the server-issued account access context
and uses the attestation state populated by
:meth:`bloks_caa_login_prepare`.
When needed, this low-level helper obtains the server-issued account
access context through :meth:`bloks_caa_login_prepare` before encrypting
the password. Set ``auto_prepare=False`` to require pre-existing state.

Returns
-------
Dict
Raw Instagram response.
"""
contact_point = username or self.username
if auto_prepare and not self.caa_aac:
preflight_state = (
self.caa_aac,
self.caa_waterfall_id,
self.attestation_challenge_nonce,
self.attestation_key_nonce,
)
try:
await self.bloks_caa_login_prepare(
username=contact_point,
domain=domain,
waterfall_id=waterfall_id,
offline_experiment_group=offline_experiment_group,
bloks_versioning_id=bloks_versioning_id,
)
except (Exception, CancelledError):
(
self.caa_aac,
self.caa_waterfall_id,
self.attestation_challenge_nonce,
self.attestation_key_nonce,
) = preflight_state
raise
if not self.caa_aac:
raise ClientError(
"CAA login requires a server-issued aac; call bloks_caa_login_prepare() before send_login_request"
)
if password.startswith("#PWD_"):
encrypted_password = password
else:
encrypted_password = await self.password_encrypt(password)
flow_id = waterfall_id or self.caa_waterfall_id or str(uuid4())
self.caa_waterfall_id = flow_id
if not self.caa_aac:
raise ClientError(
"CAA login requires a server-issued aac; call bloks_caa_login_prepare() before send_login_request"
)
# Recent CAA login screens emit short base36-like input ids. Hex-only
# UUID prefixes are accepted by the VM but can return a null-payload 404.
text_input_id = f"{uuid4().hex[:4]}ig"
Expand Down Expand Up @@ -772,7 +815,12 @@ async def bloks_caa_login(
"two_step": {},
"reason": "CAA preflight did not return account access and attestation data",
}
result = await self.bloks_caa_login_send_request(password, username=username, domain=domain)
result = await self.bloks_caa_login_send_request(
password,
username=username,
domain=domain,
auto_prepare=prepare,
)
logged_in = self.bloks_apply_login_response(result)
two_step = {}
if not logged_in and self.bloks_caa_login_needs_two_step(result):
Expand Down
2 changes: 1 addition & 1 deletion docs/usage-guide/totp.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ await cl.bloks_two_step_verification_enter_backup_code(context)
result = await cl.bloks_two_step_verification_verify_code(context, "12345678", challenge="backup_codes")
```

`await bloks_caa_login(...)` runs the complete current CAA sequence. For low-level inspection, call `await bloks_caa_login_prepare(...)` before `await bloks_caa_login_send_request(...)`; the send helper requires the account access context returned by Instagram during that preflight. `bloks_extract_two_step_verification_context(...)` extracts a legacy Bloks two-factor context when the login response exposes one.
`await bloks_caa_login(...)` runs the complete current CAA sequence. For low-level inspection, `await bloks_caa_login_send_request(...)` now runs `await bloks_caa_login_prepare(...)` automatically when the client has no server-issued account access context; existing prepared context is reused on later calls. Its domain, waterfall ID, offline experiment group, and Bloks versioning ID overrides are forwarded to that automatic preflight. Pass `auto_prepare=False` to require pre-existing state, or call the preparation helper explicitly when you need to inspect or control the preflight separately. `bloks_extract_two_step_verification_context(...)` extracts a legacy Bloks two-factor context when the login response exposes one.

`bloks_extract_login_response(...)` returns decoded `login_response`, response `headers`, cookie values, raw cookie header text, and the raw embedded object when Instagram returns a successful Bloks login payload. It returns `{}` when the response is an intermediate UI state or an error. `bloks_apply_login_response(...)` can then copy the returned authorization data and cookies into the current client session.

Expand Down
Loading
Loading