Skip to content

Instantly share code, notes, and snippets.

@brianrusso
Last active May 3, 2022 13:24
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save brianrusso/9268129 to your computer and use it in GitHub Desktop.
Save brianrusso/9268129 to your computer and use it in GitHub Desktop.
Quick and dirty example of how to authenticate to Office 365 SharePoint Online using urllib2, jinja2, cookielib. Basically you POST your user/pass to Microsoft's token service, then hand that token to SharePoint's login proper, which gives you a cookie to access SharePoint content.
import urllib2
import cookielib
import urlparse
import jinja2
from urllib2 import HTTPCookieProcessor
from lxml import etree
# Setup Jinja for SAML
JINJA_TEMPLATE_PATH = "/Users/Brian/IdeaProjects/yggdrasil/templates"
JINJA_ENVIRONMENT = jinja2.Environment(
loader=jinja2.FileSystemLoader(JINJA_TEMPLATE_PATH),
extensions=['jinja2.ext.autoescape'],
autoescape=True)
saml_template = JINJA_ENVIRONMENT.get_template('saml.xml')
# lxml needs namespaces
namespaces = {'S': "http://www.w3.org/2003/05/soap-envelope",
'wst': "http://schemas.xmlsoap.org/ws/2005/02/trust",
'wsse': "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
'wsu': "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
'saml': "urn:oasis:names:tc:SAML:1.0:assertion",
'wsp': "http://schemas.xmlsoap.org/ws/2004/09/policy",
'psf': "http://schemas.microsoft.com/Passport/SoapServices/SOAPFault"}
# This is the external microsoft service that will handout a token when given credentials
mso_auth_uri = "https://login.microsoftonline.com/extSTS.srf"
# Authentication front-end for SharePoint Online (hand it a token, gives us a cookie)
spo_auth_uri = "/_forms/default.aspx?wa=wsignin1.0"
# Change this stuff
username = "user@mydomain.onmicrosoft.com"
password = "p@ssword1"
sp_site = "https://mydomain.sharepoint.com"
sp_login_uri = urlparse.urljoin(sp_site, spo_auth_uri)
# file we want
sp_url = "https://mydomain.sharepoint.com/_vti_bin/ListData.svc/"
# xpath for binarytoken (not valid for pre-Office 365 SPO, probably doesn't apply to you)
tokenpath = "/S:Envelope/S:Body/wst:RequestSecurityTokenResponse/wst:RequestedSecurityToken/wsse:BinarySecurityToken"
# populate SAML + need cookie jar to magically handout cookies.
login_vars = {'username': username, 'password': password, 'url': sp_url}
saml = saml_template.render(login_vars)
jar = cookielib.CookieJar()
opener = urllib2.build_opener(HTTPCookieProcessor(jar))
# MSO/SPO prefers a browser UA
opener.addheaders = [
('User-Agent', 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.11) Gecko/20101012 Firefox/3.6.11')]
# 1. Get the token from MSO
f = opener.open(mso_auth_uri, saml)
tree = etree.parse(f)
# 2. Receive SAML response, store token
binarytoken = tree.xpath(tokenpath, namespaces=namespaces)[0].text
# 3. Post token to sharepoint online to get cookie
opener.open(sp_login_uri, data=binarytoken)
# 4/5 CookieJar will handle the cookies, request what we actually want.
resp = opener.open(sp_url)
# Do something interesting...
print resp.read()
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">https://login.microsoftonline.com/extSTS.srf</a:To>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:UsernameToken>
<o:Username>{{ username }}</o:Username>
<o:Password>{{ password }}</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body>
<t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<a:EndpointReference>
<a:Address>{{ url }}</a:Address>
</a:EndpointReference>
</wsp:AppliesTo>
<t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
<t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>
</t:RequestSecurityToken>
</s:Body>
</s:Envelope>
@Chris-Leeworthy
Copy link

Hi Brian,
Thanks for this code, it's the clearest I've seen yet.

I do have a problem though, when the script gets as far as "binarytoken = tree.xpath(tokenpath, namespaces=namespaces)[0].text" it crashes with the exception below.

/usr/bin/python2.7 /home/setupuser/PycharmProjects/SharePointToolKit/auth_sharepointonline.py Traceback (most recent call last):
File "/home/setupuser/PycharmProjects/SharePointToolKit/auth_sharepointonline.py", line 58, in > >
binarytoken = tree.xpath(tokenpath, namespaces=namespaces)[0].text
IndexError: list index out of range

Process finished with exit code 1

Unfortunately I don't really have enough experience with Python or SAML to get much further than this.

I don't even know if I need to make any modifications to the saml.xml before I can use the code.

Any help would be much appreciated.

@sasha42
Copy link

sasha42 commented Sep 23, 2016

Wasn't able to get this to work either, got stuck in the same place as LeewoC. I suspect that there was an update on Microsofts' side, or some other error with the authentification.

Instead I'm building a screen scraping tool that will fetch the file in a vm with Sikuli.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment