I have automated uploading files from my web site host’s server to my Office 365 Sharepoint site using scheduled cron jobs running Python scripts on my web host.
The Python scripts use Microsoft’s Azure Active Directory Library (ADAL) to authenticate off Azure Active Directory (Azure AD or ADD), and OneDrive API and Python Requests to use the authentication to upload the files to Sharepoint from my web host.
Here is the code
import adal import urllib import requests ## set variables username = '[email protected]' password = 'password' authorization_url = 'https://login.windows.net/contoso.onmicrosoft.com' # aka Authority redirect_uri = 'https://login.microsoftonline.com/login.srf' # from Azure AD application client_id = 'd84cbf4f-dc23-24d1-8a7d-08ff8359879a' # from Azure AD application file_url = 'https://contoso.sharepoint.com/_api/v2.0/drive/root:/myfoldername/myfilename.csv:/content' ## use ADAL to create token response token_response = adal.acquire_token_with_username_password( authorization_url, username, password ) ## Use ADAL to create refresh token and save as text file to reuse refresh_token = token_response['refreshToken'] refresh_token_file = open('refresh_token.txt', 'w') refresh_token_file.write(refresh_token) refresh_token_file.close() ## Get saved refresh token and use it to get new token response refresh_token = open('refresh_token.txt', 'r').read() token_response = adal.acquire_token_with_refresh_token(authorization_url, str(refresh_token)) ## get access_token from token response JSON string access_token = token_response.get('accessToken') ## create http header to send access token to authenticate headers = {'Authorization':'BEARER ' + str(access_token)} ## example to upload file upload_file = requests.put(file_url, data = open('myfilename.csv', 'rb'), headers=headers)
There are many things to consider when working with Microsoft’s APIs to work with its online services such as Office 365.
The first is how to authenticate. Microsoft is trying to move everyone to use Azure AD to do oAuth authentication. Microsoft services still have their own authentication methods but this exercise I used Azure AD.
The second is what API to use. Microsoft has recently released their Graph API that is ‘one endpoint to rule them all’. However Microsoft services still have their own API’s so while Graph API looks tempting for this exercise I used the OneDrive API.
Azure AD Authentication
The authentication will be done in two parts.
- Create Azure AD application to do the authentication for the Microsoft service(s) you want to interact with.
- Use ADAL to interact with Azure AD to do the oAuth flow.
Setup Azure AD – create application
Microsoft provides free use of Azure AD for light authentication needs. You can register and create account. Once you have your account you need to create a new application.
For my purposes I created an Azure AD native client application. Azure AD also has web application and web APIs but both require user to enter username and password in web browser. The native client application does technically also require user to enter these too but I hacked past this by using ADAL user authentication and hard coding username and password into the Python code. Since these are going onto my web host in protected directory to run as cron jobs they will be safe.
I am not going to go through the detail of creating an Azure AD application there are some good blog posts and Microsoft does good job of describing it. For example take a look at this site which has decent information about creating a new Azure AD application.
The Azure AD applications allow you to choose which Microsoft services it will be used to authenticate. Confusingly these are also called ‘applications’ too. They are represent Microsoft Services such as Office 365 Sharepoint Online, OneNote, Power BI, etc and is the place where you assign the permissions (also called ‘scopes’) that authentication will allows with that Microsoft service.
An Azure AD application might provide authentication for more than one Microsoft Service. But my native client application has only Windows Azure Active Directory permissions (which are there by default) and Office 365 Sharepoint Online permissions set to Read and write user files and Read and write items in all site collections.
After you have created your client application make sure to copy the client_id and resource_uri to use in code below. The client_id is automatically assigned and the resource_uri for a native client app can be any url and is just a unique identifier. I chose the Office 365 login url. The web applications need a real url because that is where the user will be prompted to enter credentials.
Azure Active Directory Library (ADAL)
Microsoft’s Azure Active Directory Library (ADAL) authentication libraries are created for developer’s to use with Azure AD. I used the ADAL Python SDK which was easily installed with pip install adal.
The oAuth authentication flow can seem very complex but you don’t have to worry about that if you use ADAL. ADAL uses your Azure AD application credentials (client_id, resource_uri in case of native client application) to retrieve a token response which is a text string in JSON format.
This JSON string includes the actual access token that is used to authenticate accessing Sharepoint and upload the files. You can use Python to retrieve the access token (it is a Dictionary). Then you simply put the access token into a header that will be used in the Put Request as the method of passing the access token to the OneDrive API.
ADAL also takes care of refreshing tokens which expire. In my case where the scripts are running on the server as cron jobs I want the token to refresh automatically. ADAL gets a refresh token that you can save to get a new access token when previous one expires. I actually write the refresh token to a text file on the server and refresh the access token each time code is run. I could only refresh it if the previous one expires.
OneDrive API
The OneDrive API has different configurations depending on whether you are using it to access a OneDrive Personal, OneDrive Business or Sharepoint Online account.
Be warned that the documentation for OneDrive API can be very dense and there are different ways of presenting required syntax to identify interactions. Of course the representations vary with different SDKs too. Also there are different versions of Microsofts file storage services over the years. So I recommend to focus on the newest OneDrive API and make sure you are looking at documentation relevant to newest version.
The Gotchas
ADAL Default Values
ADAL has default client_id and resource values that it uses for the username authentication. I changed these default values to match my Azure AD application.
Before changing these I was getting an Invalid audience Uri error
{“error”:”invalid_client”,”error_description”:”Invalid audience Uri ‘https:\/\/m
anagement.core.windows.net\/’.”}
This error means the url being used to create the token response was not same as the one that the file was being uploaded to.
EDIT August 21, 2016 Microsoft has updated the ADAL library so that you can specify the client id and the resource value because authentication against different services needs different client id and resource endpoint urls. That means the hack I used below is no longer required. For more details seehttps://github.com/AzureAD/azure-activedirectory-library-for-python
In ADAL’s __init__.py file look for the class _DefaultValues class at bottom of code and replace the default values:
- I changed client_id to my application’s client_id
- I changed resource from https://management.core.windows.net/ to https://tenant.sharepoint.com/
The acquire_token_with_username_password function sets these to None so they get set to default values. So this could be changed so they accept values from the code.
Sharepoint Site and Folder Paths
The OneDrive API dev documentation https://dev.onedrive.com/getting-started.htm demonstrates the different service urls:
- OneDrive – https://api.onedrive.com/v1.0
- OneDrive for Business – https://{tenant}-my.sharepoint.com/_api/v2.0
- SharePoint Online – https://{tenant}.sharepoint.com/{site-relative-path}/_api/v2.0
The {site-relative-path} notation indicates the Sharepoint site name. My site didn’t have this because it was default site. However you might have to add your site relative path.
Also the Sharepoint url for the file I was uploading looked like this:
https://contoso.sharepoint.com/Shared%20Documents/myfoldername/myfilename.csv
However you will note that the file_url in the code doesn’t make any reference to the Shared%20Documents:
https://contoso.sharepoint.com/_api/v2.0/drive/root:/myfoldername/myfilename.csv:/content
This is a really good example which got me going on the project that I am trying to implement.
Great glad it helped.