Skip to content
This repository was archived by the owner on Jun 11, 2025. It is now read-only.

Commit b21e170

Browse files
committed
Refactor Live auth, handle t at any point
1 parent 77270e1 commit b21e170

File tree

1 file changed

+67
-69
lines changed

1 file changed

+67
-69
lines changed

skpy/conn.py

Lines changed: 67 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,16 @@ def auth(self, user, pwd):
553553
return json["skypetoken"], expiry
554554

555555

556+
class LiveAuthSuccess(Exception):
557+
"""
558+
An exception used to capture the 't' value needed during Microsoft account authentication.
559+
"""
560+
561+
def __init__(self, t):
562+
super(LiveAuthSuccess, self).__init__(t)
563+
self.t = t
564+
565+
556566
class SkypeLiveAuthProvider(SkypeAuthProvider):
557567
"""
558568
An authentication provider that connects via Microsoft account authentication.
@@ -591,79 +601,67 @@ def auth(self, user, pwd):
591601
.SkypeAuthException: if the login request is rejected
592602
.SkypeApiException: if the login form can't be processed
593603
"""
594-
# Do the authentication dance.
595-
params = self.getParams()
596-
if isinstance(params, str):
597-
t = params
598-
else:
599-
params["opid"] = self.sendCreds(user, pwd, params)
600-
t = self.sendOpid(params)
601-
return self.getToken(t)
602-
603-
def getParams(self):
604-
# First, start a Microsoft account login from Skype, which will redirect to login.live.com.
605-
loginResp = self.conn("GET", "{0}/oauth/microsoft".format(SkypeConnection.API_LOGIN),
606-
params={"client_id": "578134", "redirect_uri": "https://web.skype.com"})
607-
tField = BeautifulSoup(loginResp.text, "html.parser").find(id="t")
604+
try:
605+
self.getT(user, pwd)
606+
except LiveAuthSuccess as ex:
607+
return self.getToken(ex.t)
608+
609+
def check(self, resp):
610+
page = BeautifulSoup(resp.text, "html.parser")
611+
# Look for the 't' value we need to exchange for a Skype token, which might turn up at any stage.
612+
tField = page.find(id="t")
608613
if tField is not None:
609-
# We've already got an existing session, no further steps needed.
610-
return tField.get("value")
611-
# This is inside some embedded JavaScript, so can't easily parse with BeautifulSoup.
612-
ppftReg = re.search(r"""<input.*?name="PPFT".*?value="(.*?)""" + "\"", loginResp.text)
613-
if not ppftReg:
614-
raise SkypeApiException("Couldn't retrieve PPFT from login form", loginResp)
615-
if "MSPRequ" not in loginResp.cookies or "MSPOK" not in loginResp.cookies:
616-
raise SkypeApiException("Couldn't retrieve MSPRequ/MSPOK cookies", loginResp)
617-
return {"MSPRequ": loginResp.cookies.get("MSPRequ"),
618-
"MSPOK": loginResp.cookies.get("MSPOK"),
619-
"PPFT": ppftReg.group(1)}
620-
621-
def sendCreds(self, user, pwd, params):
622-
# Now pass the login credentials over.
623-
loginResp = self.conn("POST", "{0}/ppsecure/post.srf".format(SkypeConnection.API_MSACC),
624-
params={"wa": "wsignin1.0", "wp": "MBI_SSL",
625-
"wreply": "https://lw.skype.com/login/oauth/proxy?client_id=578134&site_name="
626-
"lw.skype.com&redirect_uri=https%3A%2F%2Fweb.skype.com%2F"},
627-
cookies={"MSPRequ": params["MSPRequ"], "MSPOK": params["MSPOK"],
628-
"CkTst": "G{0}".format(int(time.time() * 1000))},
629-
data={"login": user, "passwd": pwd,
630-
"PPFT": params["PPFT"], "loginoptions": "3"})
631-
opid = re.search(r"""opid=([A-Z0-9]+)""", loginResp.text, re.I)
632-
if opid:
633-
return opid.group(1)
634-
loginPage = BeautifulSoup(loginResp.text, "html.parser")
635-
for form in loginPage.findAll("form"):
614+
raise LiveAuthSuccess(tField.get("value"))
615+
# Look for an error message within the response.
616+
errReg = re.search(r"sErrTxt:'([^'\\]*(\\.[^'\\]*)+)'", resp.text)
617+
if errReg:
618+
errMsg = re.sub(r"<.*?>", "", errReg.group(1)).replace("\\'", "'").replace("\\\\", "\\")
619+
raise SkypeApiException(errMsg, resp)
620+
# Look for two-factor authentication device information (a non-empty array of factors) that we can't handle.
621+
if re.search(r"\bV:\s*\[\s*{", resp.text):
622+
raise SkypeAuthException("Two-factor authentication unsupported", resp)
623+
# Look for a user consent form, meaning the user needs to accept terms or follow account security steps.
624+
for form in page.findAll("form"):
636625
if form["name"] == "fmHF":
637626
url = form["action"].split("?", 1)[0]
638627
raise SkypeAuthException("Account action required ({0}), login with a web browser first"
639-
.format(url), loginResp)
640-
else:
641-
raise SkypeApiException("Couldn't retrieve opid field from login response", loginResp)
642-
643-
def sendOpid(self, params):
644-
# Now repeat with the opid parameter.
645-
loginResp = self.conn("POST", "{0}/ppsecure/post.srf".format(SkypeConnection.API_MSACC),
646-
params={"wa": "wsignin1.0", "wp": "MBI_SSL",
647-
"wreply": "https://lw.skype.com/login/oauth/proxy?client_id=578134&site_name="
648-
"lw.skype.com&redirect_uri=https%3A%2F%2Fweb.skype.com%2F"},
649-
cookies={"MSPRequ": params["MSPRequ"],
650-
"MSPOK": params["MSPOK"],
651-
"CkTst": str(int(time.time() * 1000))},
652-
data={"opid": params["opid"],
653-
"site_name": "lw.skype.com",
654-
"oauthPartner": "999",
655-
"client_id": "578134",
656-
"redirect_uri": "https://web.skype.com",
657-
"PPFT": params["PPFT"],
658-
"type": "28"})
659-
tField = BeautifulSoup(loginResp.text, "html.parser").find(id="t")
660-
if tField is None:
661-
err = re.search(r"sErrTxt:'([^'\\]*(\\.[^'\\]*)*)'", loginResp.text)
662-
errMsg = "Couldn't retrieve t field from login response"
663-
if err:
664-
errMsg = re.sub(r"<.*?>", "", err.group(1)).replace("\\'", "'").replace("\\\\", "\\") or errMsg
665-
raise SkypeApiException(errMsg, loginResp)
666-
return tField.get("value")
628+
.format(url), resp)
629+
# No common elements, return the response for further processing.
630+
return resp
631+
632+
def getT(self, user, pwd):
633+
# Stage 1: Start a Microsoft account login from Skype, which will redirect to login.live.com.
634+
stage1Resp = self.check(self.conn("GET", "{0}/oauth/microsoft".format(SkypeConnection.API_LOGIN),
635+
params={"client_id": "578134", "redirect_uri": "https://web.skype.com"}))
636+
# This is inside some embedded JavaScript, so can't easily parse with BeautifulSoup.
637+
ppftReg = re.search(r"""<input.*?name="PPFT".*?value="(.*?)""" + "\"", stage1Resp.text)
638+
if not ppftReg:
639+
raise SkypeApiException("Couldn't retrieve PPFT from login form", stage1Resp)
640+
ppft = ppftReg.group(1)
641+
if "MSPRequ" not in stage1Resp.cookies or "MSPOK" not in stage1Resp.cookies:
642+
raise SkypeApiException("Couldn't retrieve MSPRequ/MSPOK cookies", stage1Resp)
643+
# Prepare the Live login page request parameters.
644+
params = {"wa": "wsignin1.0", "wp": "MBI_SSL",
645+
"wreply": "https://lw.skype.com/login/oauth/proxy?client_id=578134&site_name="
646+
"lw.skype.com&redirect_uri=https%3A%2F%2Fweb.skype.com%2F"}
647+
cookies = {"MSPRequ": stage1Resp.cookies.get("MSPRequ"), "MSPOK": stage1Resp.cookies.get("MSPOK")}
648+
# Stage 2: Submit the user's credentials.
649+
stage2Resp = self.check(self.conn("POST", "{0}/ppsecure/post.srf".format(SkypeConnection.API_MSACC),
650+
params=params,
651+
cookies=dict(cookies, CkTst="G{0}".format(int(time.time() * 1000))),
652+
data={"login": user, "passwd": pwd, "PPFT": ppft, "loginoptions": "3"}))
653+
opidReg = re.search(r"""opid=([A-Z0-9]+)""", stage2Resp.text, re.I)
654+
if not opidReg:
655+
raise SkypeApiException("Couldn't retrieve opid field from login response", stage2Resp)
656+
# Stage 3: Repeat with the 'opid' parameter.
657+
stage3Resp = self.check(self.conn("POST", "{0}/ppsecure/post.srf".format(SkypeConnection.API_MSACC),
658+
params=params,
659+
cookies=dict(cookies, CkTst="G{0}".format(int(time.time() * 1000))),
660+
data={"opid": opidReg.group(1), "PPFT": ppft, "site_name": "lw.skype.com",
661+
"oauthPartner": "999", "client_id": "578134",
662+
"redirect_uri": "https://web.skype.com", "type": "28"}))
663+
# No check matches, and no further actions we can take.
664+
raise SkypeApiException("Couldn't retrieve t field from login response", stage3Resp)
667665

668666
def getToken(self, t):
669667
# Now exchange the 't' value for a Skype token.

0 commit comments

Comments
 (0)