Skip to content

Commit de4d579

Browse files
authored
Merge pull request #297 from wreiner/master
Fix sharepoint saml auth issue #272
2 parents d1083b9 + 476bbce commit de4d579

File tree

3 files changed

+44
-32
lines changed

3 files changed

+44
-32
lines changed

office365/runtime/auth/providers/saml_token_provider.py

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import os
22
import uuid
33
from xml.etree import ElementTree
4+
import xml.dom.minidom as minidom
5+
from urllib.parse import urlparse
6+
7+
from datetime import datetime, timezone, timedelta
8+
49

510
import requests
611
import requests.utils
@@ -106,40 +111,35 @@ def get_last_error(self):
106111

107112
def acquire_service_token_from_adfs(self, adfs_url, username, password):
108113
logger = self.logger(self.acquire_service_token_from_adfs.__name__)
114+
115+
now = datetime.now(tz=timezone.utc)
116+
created = now.astimezone(timezone.utc).isoformat('T')[:-9]+'Z'
117+
expires = (now + timedelta(minutes=10)).astimezone(timezone.utc).isoformat('T')[:-9]+'Z'
118+
109119
payload = self._prepare_request_from_template('FederatedSAML.xml', {
110120
'auth_url': adfs_url,
121+
'message_id': str(uuid.uuid4()),
111122
'username': username,
112123
'password': password,
113-
'message_id': str(uuid.uuid4()),
114-
'created': self.__sts_profile.created,
115-
'expires': self.__sts_profile.expires,
124+
'created': created,
125+
'expires': expires,
116126
'issuer': self.__sts_profile.federationTokenIssuer
117127
})
128+
118129
response = requests.post(adfs_url, data=payload,
119130
headers={'Content-Type': 'application/soap+xml; charset=utf-8'})
131+
dom = minidom.parseString(response.content.decode())
132+
assertion_node = dom.getElementsByTagNameNS("urn:oasis:names:tc:SAML:1.0:assertion", 'Assertion')[0].toxml()
133+
120134
try:
121-
xml = ElementTree.fromstring(response.content)
122-
# 1.find assertion
123-
assertion_node = xml.find(
124-
'{0}Body/{1}RequestSecurityTokenResponse/{1}RequestedSecurityToken/{2}Assertion'.format(
125-
self.__ns_prefixes['s'], self.__ns_prefixes['wst'], self.__ns_prefixes['saml']))
126-
if assertion_node is None:
127-
self.error = 'Cannot get security assertion for user {0} from {1}'.format(username, adfs_url)
128-
logger.error(self.error)
129-
return None
130-
# 2. prepare & submit token request
131-
self.__sts_profile.signInPage = '_vti_bin/idcrl.svc/'
132-
self.__sts_profile.securityTokenServicePath = 'rst2.srf'
133-
template = self._prepare_request_from_template('RST2.xml', {
134-
'auth_url': self.__sts_profile.authorityUrl,
135-
'serviceTokenUrl': self.__sts_profile.security_token_service_url
135+
self.tenant = urlparse(self.__sts_profile.authorityUrl).netloc
136+
137+
payload = self._prepare_request_from_template('RST2.xml', {
138+
'auth_url': self.tenant,
139+
'serviceTokenUrl': self.__sts_profile.security_token_service_url,
140+
'assertion_node': assertion_node
136141
})
137-
template_xml = ElementTree.fromstring(template)
138-
security_node = template_xml.find(
139-
'{0}Header/{1}Security'.format(self.__ns_prefixes['s'], self.__ns_prefixes['wsse']))
140142

141-
security_node.insert(1, assertion_node)
142-
payload = ElementTree.tostring(template_xml).decode()
143143
# 3. get token
144144
response = requests.post(self.__sts_profile.security_token_service_url, data=payload,
145145
headers={'Content-Type': 'application/soap+xml'})
@@ -212,6 +212,7 @@ def _acquire_authentication_cookie(self, security_token, federated=False):
212212
:type security_token: str
213213
"""
214214
logger = self.logger(self._acquire_authentication_cookie.__name__)
215+
215216
session = requests.session()
216217
logger.debug_secrets("session: %s\nsession.post(%s, data=%s)", session, self.__sts_profile.signin_page_url,
217218
security_token)
@@ -222,7 +223,8 @@ def _acquire_authentication_cookie(self, security_token, federated=False):
222223
headers={'Content-Type': 'application/x-www-form-urlencoded'})
223224
else:
224225
self._auth_cookies['SPOIDCRL'] = None
225-
session.get(self.__sts_profile.signin_page_url,
226+
idcrlEndpoint = "https://{}/_vti_bin/idcrl.svc/".format(self.tenant)
227+
session.get(idcrlEndpoint,
226228
headers={
227229
'User-Agent': 'Office365 Python Client',
228230
'X-IDCRL_ACCEPTED': 't',

office365/runtime/auth/providers/templates/FederatedSAML.xml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,32 @@
44
<wsa:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</wsa:Action>
55
<wsa:To s:mustUnderstand="1">{auth_url}</wsa:To>
66
<wsa:MessageID>{message_id}</wsa:MessageID>
7+
<ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
8+
<ps:HostingApp>Managed IDCRL</ps:HostingApp>
9+
<ps:BinaryVersion>6</ps:BinaryVersion>
10+
<ps:UIVersion>1</ps:UIVersion>
11+
<ps:Cookies></ps:Cookies>
12+
<ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
13+
</ps:AuthInfo>
714
<wsse:Security>
815
<wsse:UsernameToken wsu:Id="user">
916
<wsse:Username>{username}</wsse:Username>
1017
<wsse:Password>{password}</wsse:Password>
1118
</wsse:UsernameToken>
19+
<wsu:Timestamp Id="Timestamp">
20+
<wsu:Created>{created}</wsu:Created><wsu:Expires>{expires}</wsu:Expires>
21+
</wsu:Timestamp>
1222
</wsse:Security>
1323
</s:Header>
1424
<s:Body>
1525
<wst:RequestSecurityToken Id="RST0">
1626
<wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>
1727
<wsp:AppliesTo>
1828
<wsa:EndpointReference>
19-
<wsa:Address>urn:federation:MicrosoftOnline</wsa:Address>
29+
<wsa:Address>{issuer}</wsa:Address>
2030
</wsa:EndpointReference>
2131
</wsp:AppliesTo>
2232
<wst:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</wst:KeyType>
2333
</wst:RequestSecurityToken>
2434
</s:Body>
25-
</s:Envelope>
35+
</s:Envelope>
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope"
2+
<S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope"
33
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
44
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
55
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
6-
xmlns:wsa="http://www.w3.org/2005/08/addressing"
6+
xmlns:wsa="http://www.w3.org/2005/08/addressing"
77
xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
88
<S:Header>
99
<wsa:Action S:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</wsa:Action>
10-
<wsa:To S:mustUnderstand="1">https://login.microsoftonline.com/rst2.srf</wsa:To>
10+
<wsa:To S:mustUnderstand="1">{serviceTokenUrl}</wsa:To>
1111
<ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/LiveID/SoapServices/v1" Id="PPAuthInfo">
1212
<ps:BinaryVersion>5</ps:BinaryVersion>
1313
<ps:HostingApp>Managed IDCRL</ps:HostingApp>
1414
</ps:AuthInfo>
15-
<wsse:Security></wsse:Security>
15+
<wsse:Security>{assertion_node}</wsse:Security>
1616
</S:Header>
1717
<S:Body>
1818
<wst:RequestSecurityToken xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust" Id="RST0">
1919
<wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>
2020
<wsp:AppliesTo>
2121
<wsa:EndpointReference>
22-
<wsa:Address>sharepoint.com</wsa:Address>
22+
<wsa:Address>{auth_url}</wsa:Address>
2323
</wsa:EndpointReference>
2424
</wsp:AppliesTo>
2525
<wsp:PolicyReference URI="MBI"></wsp:PolicyReference>
2626
</wst:RequestSecurityToken>
2727
</S:Body>
28-
</S:Envelope>
28+
</S:Envelope>

0 commit comments

Comments
 (0)