Access Sharepoint Online With Entra Registration
At work, I was recently asked to allow an on-premise Python application access to SharePoint Online (SPO).
The now-depreciated way would be to go to your SPO site, append /_layouts/15/appregnew.aspx
to the site's name (so it would look something like https://tenant.sharepoint.com/sites/TestSiteforPythonAPIAccess/_layouts/15/appregnew.aspx
). This is creating an app registration on the specified SPO site, but this method is being depricated: https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/retirement-announcement-for-add-ins. It works in my production tenant, but fails in my developer space.
My user didn't specify how they intended to authenticate to SPO, so with a little bit of digging, I find https://github.com/vgrem/Office365-REST-Python-Client as a seemingly well supported package that handles this from Python.
Interactive Authentication
If the tool we're using only requires interactive authentication, then this becomes relatively easy:
- Create an App Registration in Entra. Call it whatever you want. Collect the "Application (client) ID" and "Directory (tenant) ID" for later:
And.. that's it. App Registrations come with a default Graph User.Read
right, which is all we need at this point.
-
Add the user(s) to the SPO site. This is where we're manging the access to the resources for the users.
-
Proft? My test Python is pretty minimal:
1test_site_url = "https://tenant.sharepoint.com/sites/TestSiteforPythonAPIAccess"
2test_tenant = "b59ed669-e556-4c24-xxxx-xxxxxxxxxxxx"
3test_client_id = "0e8f0030-f25d-4ba9-ae85-415ebbd2dda3"
4
5ctx = ClientContext(test_site_url).with_interactive(test_tenant, test_client_id)
6me = ctx.web.current_user.get().execute_query()
7print(me)
8web = ctx.web.get().execute_query()
9print(web)
If we did our stuff right, then thisn should print out my name, and the name of the SPO site.
Unattended Interaction
This gets a bit more spicy.
- Generate a self-signed certificiate that you will use for authentication. Something like
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout privateKey.key -out selfsigncert.crt
, followed bycat selfsigncert.crt privateKey.key > selfsigncert.pem
should do the trick. - In your Entra App Registration, under Manage, Certificates & Secrets, upload the created
selfsigncert.pem
in the Certificates tab: - Collect the Thumbprint from this view (though we can get it with openssl:
openssl x509 -in selfsignedcert.pem -noout -fingerprint
) - Still in the App Registration, under API Permissions, add SharePoint, Application,
Sites.Selected
. Grant Admin consent:Sites.Selected
API as well. This does not work with the Python library selected. - Collect the Sharepoint Site ID from your site:
https://tenant.sharepoint.com/sites/TestSiteforPythonAPIAccess/_api/site/id
. Look for the XML element with type type ofEdm.Guid
. Collect this too.
To summarize, we need:
- The SPO Site ID
- The Entra App Registration Client ID
- The Entra App Registration name
- The Entra Tenant ID
- Certificate PEM
- Certificate thumbprint
- Head over to the MS Graph Explorer and log in as a user with Global Admin access (or I think Site collection Admin).
- Query
GET
tohttps://graph.microsoft.com/v1.0/sites/<SPO Site ID>/permissions
. You should get a response, with an empty value:200
response code, you probably want to address the error first. Check:
- Are you logged in? If not, you are probably querying a default set of data
- Have you specified the Site ID in the URL?
- Compose a
POST
to the same URL with the following JSON:
1{
2 "roles": [
3 "fullcontrol"
4 ],
5 "grantedToIdentities": [
6 {
7 "application": {
8 "id": "0e8f0030-f25d-4ba9-xxxx-xxxxxxxxxxx",
9 "displayName": "PythonTest"
10 }
11 }
12 ]
13}
Replace the app.id
and app.displayName
with the real information from the Entra App Registration. For some reason, you must have the displayName.
- Re-run the
GET
you did in step 7. Hopefully you see your granted role.
Steps 6 through 9 can also be accomodated with the PnP.PowerShell modules. This is arguably a bit more user-friendly, but takes a bit more to setup. See https://pnp.github.io/powershell/cmdlets/Connect-PnPOnline.html, which will also need https://pnp.github.io/powershell/articles/registerapplication.html (if you have not already set up PnP in your tenant) and PowerShell 7.
Anyhow. On to test.
1test_site_url = "https://tenant.sharepoint.com/sites/TestSiteforPythonAPIAccess"
2test_tenant = "b59ed669-e556-4c24-xxxx-xxxxxxxxxxxx"
3test_client_id = "0e8f0030-f25d-4ba9-ae85-415ebbd2dda3"
4
5cert_settings = {
6 'tenant': test_tenant,
7 'client_id': test_client_id,
8 'thumbprint': "EDC90C2DA540BD925F784F7C9C6CA062411A20C6",
9 'cert_path': "{0}/selfsignedcert.pem".format(os.path.dirname(__file__)),
10}
11
12ctx = ClientContext(test_site_url).with_client_certificate(**cert_settings)
13
14current_web = ctx.web
15ctx.load(current_web)
16ctx.execute_query()
17print("{0}".format(current_web.url))
18
19me = ctx.web.current_user.get().execute_query()
20print(me)
21web = ctx.web.get().execute_query()
22print(web)
Since we're not running as an identified user now, you should see an output of the current web URL, the user as "SharePoint App", and the site name.
Change the test site URL to another SPO site, and verify that you get a 403.