|
7 | 7 | import hashlib
|
8 | 8 | import base64
|
9 | 9 | from pprint import pformat
|
| 10 | +from xml.etree import ElementTree |
10 | 11 |
|
11 | 12 | from bs4 import BeautifulSoup
|
12 | 13 | import requests
|
@@ -124,6 +125,7 @@ def externalCall(cls, method, url, codes=(200, 201, 204, 207), **kwargs):
|
124 | 125 |
|
125 | 126 | API_LOGIN = "https://login.skype.com/login"
|
126 | 127 | API_MSACC = "https://login.live.com"
|
| 128 | + API_EDGE = "https://edge.skype.com/rps/v1/rps/skypetoken" |
127 | 129 | API_USER = "https://api.skype.com"
|
128 | 130 | API_PROFILE = "https://profile.skype.com/profile/v1"
|
129 | 131 | API_OPTIONS = "https://options.skype.com/options/v1/users/self/options"
|
@@ -363,7 +365,7 @@ def liveLogin(self, user, pwd):
|
363 | 365 | .SkypeAuthException: if the login request is rejected
|
364 | 366 | .SkypeApiException: if the login form can't be processed
|
365 | 367 | """
|
366 |
| - self.tokens["skype"], self.tokenExpiry["skype"] = SkypeLiveAuthProvider(self).auth(user, pwd) |
| 368 | + self.tokens["skype"], self.tokenExpiry["skype"] = SkypeSOAPAuthProvider(self).auth(user, pwd) |
367 | 369 | self.getUserId()
|
368 | 370 | self.getRegToken()
|
369 | 371 |
|
@@ -592,6 +594,89 @@ def getToken(self, t):
|
592 | 594 | return (token, expiry)
|
593 | 595 |
|
594 | 596 |
|
| 597 | +class SkypeSOAPAuthProvider(SkypeAuthProvider): |
| 598 | + |
| 599 | + template = """ |
| 600 | + <Envelope xmlns='http://schemas.xmlsoap.org/soap/envelope/' |
| 601 | + xmlns:wsse='http://schemas.xmlsoap.org/ws/2003/06/secext' |
| 602 | + xmlns:wsp='http://schemas.xmlsoap.org/ws/2002/12/policy' |
| 603 | + xmlns:wsa='http://schemas.xmlsoap.org/ws/2004/03/addressing' |
| 604 | + xmlns:wst='http://schemas.xmlsoap.org/ws/2004/04/trust' |
| 605 | + xmlns:ps='http://schemas.microsoft.com/Passport/SoapServices/PPCRL'> |
| 606 | + <Header> |
| 607 | + <wsse:Security> |
| 608 | + <wsse:UsernameToken Id='user'> |
| 609 | + <wsse:Username>{}</wsse:Username> |
| 610 | + <wsse:Password>{}</wsse:Password> |
| 611 | + </wsse:UsernameToken> |
| 612 | + </wsse:Security> |
| 613 | + </Header> |
| 614 | + <Body> |
| 615 | + <ps:RequestMultipleSecurityTokens Id='RSTS'> |
| 616 | + <wst:RequestSecurityToken Id='RST0'> |
| 617 | + <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType> |
| 618 | + <wsp:AppliesTo> |
| 619 | + <wsa:EndpointReference> |
| 620 | + <wsa:Address>wl.skype.com</wsa:Address> |
| 621 | + </wsa:EndpointReference> |
| 622 | + </wsp:AppliesTo> |
| 623 | + <wsse:PolicyReference URI='MBI_SSL'></wsse:PolicyReference> |
| 624 | + </wst:RequestSecurityToken> |
| 625 | + </ps:RequestMultipleSecurityTokens> |
| 626 | + </Body> |
| 627 | + </Envelope> |
| 628 | + """ |
| 629 | + |
| 630 | + @staticmethod |
| 631 | + def encode(value): |
| 632 | + return value.replace("&", "&").replace("<", "<").replace(">", ">") |
| 633 | + |
| 634 | + def getSecToken(self, user, pwd): |
| 635 | + loginResp = self.conn("POST", "{0}/RST.srf".format(SkypeConnection.API_MSACC), |
| 636 | + data=self.template.format(self.encode(user), self.encode(pwd))) |
| 637 | + loginData = ElementTree.fromstring(loginResp.text) |
| 638 | + token = None |
| 639 | + for node in loginData.iter(): |
| 640 | + tag = node.tag.split("}", 1)[-1] |
| 641 | + if tag == "Fault": |
| 642 | + code = msg = None |
| 643 | + for fnode in node: |
| 644 | + ftag = fnode.tag.split("}", 1)[-1] |
| 645 | + if ftag == "faultcode": |
| 646 | + code = fnode.text |
| 647 | + elif ftag == "faultstring": |
| 648 | + msg = fnode.text |
| 649 | + raise SkypeAuthException("{} - {}".format(code, msg), loginResp) |
| 650 | + elif tag == "BinarySecurityToken": |
| 651 | + token = node.text |
| 652 | + if not token: |
| 653 | + raise SkypeAuthException("Couldn't retrieve security token from login response", loginResp) |
| 654 | + return token |
| 655 | + |
| 656 | + def exchangeToken(self, token): |
| 657 | + edgeResp = self.conn("POST", SkypeConnection.API_EDGE, |
| 658 | + data={"partner": 999, "access_token": token, "scopes": "client"}) |
| 659 | + try: |
| 660 | + edgeData = edgeResp.json() |
| 661 | + except ValueError: |
| 662 | + raise SkypeAuthException("Couldn't parse edge response body", edgeResp) |
| 663 | + if "skypetoken" in edgeData: |
| 664 | + token = edgeData["skypetoken"] |
| 665 | + expiry = None |
| 666 | + if "expiresIn" in edgeData: |
| 667 | + expiry = datetime.fromtimestamp(int(time.time()) + int(edgeData["expiresIn"])) |
| 668 | + return (token, expiry) |
| 669 | + elif "status" in edgeData: |
| 670 | + status = edgeData["status"] |
| 671 | + raise SkypeAuthException("{} - {}".format(status.get("code"), status.get("text")), edgeResp) |
| 672 | + else: |
| 673 | + raise SkypeAuthException("Couldn't retrieve token from edge response", edgeResp) |
| 674 | + |
| 675 | + def auth(self, user, pwd): |
| 676 | + token = self.getSecToken(user, pwd) |
| 677 | + return self.exchangeToken(token) |
| 678 | + |
| 679 | + |
595 | 680 | class SkypeGuestAuthProvider(SkypeAuthProvider):
|
596 | 681 | """
|
597 | 682 | An authentication provider that connects and joins a public conversation via a join URL.
|
|
0 commit comments