| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- """Experiment: does getByRefresh extend the token's validity?
- Method:
- 1. Decode current JWT payload (iat/exp/refresh_exp) BEFORE the call
- 2. Call POST user-api.996hezi.com/v1/UserInfo/getByRefresh with the token
- 3. Dump the FULL raw response (every key — last time we only looked at nick/id)
- 4. Decode JWT AFTER — compare; look for ANY new token string anywhere
- in the response (data.token / data.userinfo.token / set-cookie header etc.)
- """
- import asyncio
- import base64
- import json
- import sys
- from datetime import datetime, timezone
- from pathlib import Path
- _HERE = Path(__file__).resolve().parent
- sys.path.insert(0, str(_HERE.parent / "src"))
- from cq2_sdk.env_loader import load_env
- load_env(_HERE.parent / ".env")
- from cq2_sdk.login import Box996Login, ManualGeetestSolver
- from cq2_sdk.store import UserStore, connection
- def decode_jwt(token: str) -> dict:
- parts = token.split(".")
- pad = parts[1] + "=" * (-len(parts[1]) % 4)
- return json.loads(base64.urlsafe_b64decode(pad))
- def show_life(payload: dict, label: str) -> None:
- def ts(k):
- v = payload.get(k)
- return f"{datetime.fromtimestamp(v, tz=timezone.utc).isoformat()[:16]}" if v else "-"
- print(f" [{label}] iat={ts('iat')} exp={ts('exp')} refresh_exp={ts('refresh_exp')}")
- def hunt_tokens(obj, path="") -> list:
- """Recursively find any long eyJ... strings anywhere in the response."""
- found = []
- if isinstance(obj, dict):
- for k, v in obj.items():
- found += hunt_tokens(v, f"{path}.{k}")
- elif isinstance(obj, list):
- for i, v in enumerate(obj):
- found += hunt_tokens(v, f"{path}[{i}]")
- elif isinstance(obj, str) and obj.startswith("eyJ") and len(obj) > 100:
- found.append((path, obj))
- return found
- async def main() -> int:
- with connection() as conn:
- u = UserStore(conn).get_by_username("longwu988")
- old_token = u.token
- print("=== 1) JWT BEFORE ===")
- p_before = decode_jwt(old_token)
- show_life(p_before, "before")
- print("\n=== 2) call getByRefresh ===")
- box = Box996Login(geetest=ManualGeetestSolver(), device_id="00:15:5d:0a:04:00")
- box.token = old_token
- r = await box._request(
- "POST", "https://user-api.996hezi.com", "/v1/UserInfo/getByRefresh"
- )
- print(f" code={r.get('code')} msg={r.get('msg', '')!r}")
- print("\n=== 3) FULL raw response (every key) ===")
- print(json.dumps(r, ensure_ascii=False, indent=2)[:3000])
- print("\n=== 4) token hunt: any NEW eyJ... anywhere in response? ===")
- hits = hunt_tokens(r)
- if not hits:
- print(" (none found)")
- new_token = None
- for path, tok in hits:
- is_new = tok != old_token
- print(f" {path}: len={len(tok)} NEW={is_new}")
- if is_new and ".eyJ" in tok:
- new_token = tok
- try:
- show_life(decode_jwt(tok), "candidate-new")
- except Exception as e:
- print(f" (decode failed: {e})")
- print("\n=== verdict ===")
- if new_token:
- p_after = decode_jwt(new_token)
- show_life(p_after, "after")
- gained = p_after.get("exp", 0) - p_before.get("exp", 0)
- print(f" NEW TOKEN issued! exp extended by {gained/86400:.1f} days")
- with connection() as conn:
- UserStore(conn).update_token(user_id=u.id, token=new_token)
- print(" → saved to MySQL")
- else:
- exp_left = (p_before.get("exp", 0) - 1789700000) / 86400 # vs ~now
- print(" getByRefresh does NOT issue a new token.")
- print(f" current token still valid until exp (above); plan: re-login via A2 before expiry.")
- return 0
- if __name__ == "__main__":
- sys.exit(asyncio.run(main()))
|