shareplum
shareplum copied to clipboard
Authenticate as a Service/Daemon with a Certificate OR plugable authentication
I am loving the design perspective of this package. It's much cleaner than other solutions I have been looking at using. The problem I've ran into is authenticating as a Daemon/Service.
Using the msal library it is possible to authenticate into Office365 sharepoint site using a certificate. What is returned is the Bearer Token.
What would be the steps to use a token when making requests?
I tried implementing the following to get the authorization cookies to see if that would work.
import msal
import requests
from requests.cookies import RequestsCookieJar
tenant = '' # fill in
sharepoint_api_certificate_thumbprint = '' # fill in
sharepoint_api_private_file_location = '' # fill in
sharepoint_api_key = '' # fill in
sharepoint_base_url = '' # fill in
def client_certificate_auth() -> dict:
"""Get the Token with client secret
https://github.com/Azure-Samples/ms-identity-python-daemon/tree/master/1-Call-MsGraph-WithSecret
"""
scope = ["https://graph.microsoft.com/.default"]
private_key_details = {
'thumbprint': sharepoint_api_certificate_thumbprint,
'private_key': open(sharepoint_api_private_file_location).read(),
}
app = msal.ConfidentialClientApplication(
authority=f"https://login.microsoftonline.com/{tenant}",
client_id=sharepoint_api_key,
client_credential=private_key_details,
)
result = app.acquire_token_silent(scopes=scope, account=None)
if not result:
result = app.acquire_token_for_client(scopes=scope)
return result
def get_cookies(access_header: dict) -> RequestsCookieJar:
url = f'{sharepoint_base_url}/_forms/default.aspx?wa=wsignin1.0'
response = requests.post(url, access_header['access_token'])
response.raise_for_status()
return response.cookies
Using the functions above I tried to get the cookies and plug those into the library
from shareplum import Office365, Site
from shareplum.site import Version
# from above
access_header = client_certificate_auth()
authcookie = get_cookies(access_header)
file_name = '' # fill in
file_folder_name = '' # fill in
sharepoint = Site(f"{sharepoint_base_url }/sites/{tenant}", version=Version.v2016, authcookie=authcookie)
folder = sharepoint .Folder(file_folder_name)
file = folder.get_file(file_name)
The error returned (with tenant
, site
, and file locations redacted
Traceback (most recent call last):
File "...\lib\site-packages\shareplum\request_helper.py", line 17, in post
response.raise_for_status()
File "...\lib\site-packages\requests\models.py", line 941, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://{tenant}.sharepoint.com/sites/{site}/_vti_bin/lists.asmx
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "...\lib\site-packages\shareplum\site.py", line 547, in Site
return _Site365(site_url,
File "...\lib\site-packages\shareplum\site.py", line 404, in __init__
super().__init__(site_url, auth, authcookie, verify_ssl, ssl_version, huge_tree, timeout)
File "...\lib\site-packages\shareplum\site.py", line 96, in __init__
self.users = self.get_users()
File "...\lib\site-packages\shareplum\site.py", line 339, in get_users
response = post(self._session,
File "...\lib\site-packages\shareplum\request_helper.py", line 20, in post
raise ShareplumRequestError("Shareplum HTTP Post Failed", err)
shareplum.errors.ShareplumRequestError: Shareplum HTTP Post Failed : 403 Client Error: Forbidden for url: https://{tenant}.sharepoint.com/sites/{site}/_vti_bin/lists.asmx
I'm not really familiar with SharePoint authentication to be honest. This is a bit beyond my understanding. I don't think I can help.
Did this go anywhere? any code written or PR? I'm surprised there isnt more interest in service account auth?
Its something I'm interested in and can potentially submit a PR if it hasnt already been done
Hi @daniel-butler did you find any solution to this error?
No, I couldn't get it to work. I instead used Office365-REST-Python-Client with the app certificate authentication flow. Here is the documentation from Microsoft. I tried to get the authentication headers using this flow to use share plum instead, but wasn't able to get it to work. After a while, I gave up and implemented the code below.
pip install Office365-REST-Python-Client
Below is the code.
from office365.sharepoint.client_context import ClientContext
...
ctx = ClientContext.connect_with_certificate(
base_url=f'{settings._base_url}/sites/XXXXX',
client_id=settings._api_key,
thumbprint=settings._api_certificate_thumbprint,
cert_path=settings._api_private_file_location,
)
target_folder = ctx.web.get_folder_by_server_relative_url(remote)
target_folder.upload_file(local.name, file_content)
ctx.execute_query()
Not sure why I didn't see the earlier messages. I would have said something sooner!