Skip to content

Instantly share code, notes, and snippets.

@brianrusso
Last active May 3, 2022 13:24
  • Star 11 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
Star You must be signed in to star a gist
Embed
What would you like to do?
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