pythonazureazure-active-directoryazure-web-app-servicesharepoint-rest-api

Use Python to call SharePoint API get 401 response while I have the token


I have registered my App in Azure with API permission as below:

enter image description here

Here is my python code.

import requests
from msal import ConfidentialClientApplication

client_id = "xxxxxxxxxxxxxxxxxxxxx"
client_secret = "yyyyyyyyyyyyyyyyyyyyy"
tenant_id = "tttttttttttttttttttttttttttttttttttttttt"
site_url = "https://{mytenent}.sharepoint.com/sites/mysite"
resource = "https://{mytenent}"

# Authenticate using client ID and secret
authority = f"https://login.microsoftonline.com/{tenant_id}"
app = ConfidentialClientApplication(
    client_id=client_id,
    authority=authority,
    client_credential=client_secret,
)

scope=[resource + "/.default"]
token_response = app.acquire_token_for_client(scopes=scope)


access_token = token_response.get("access_token") #token_response["access_token"]
print(access_token)

endpoint_url = f"{site_url}/_api/web"
headers = {
    "Authorization": "Bearer " + access_token,
    "Accept": "application/json;odata=verbose",
    "Content-Type": "application/json;odata=verbose",
}
print(endpoint_url)
response = requests.get(endpoint_url, headers=headers)

# Check for errors in the SharePoint API response
if response.status_code == 200:
    data = response.json()
    print("SharePoint Site Title:", data["d"]["Title"])
else:
    print("SharePoint API Error:")
    print("Status Code:", response.status_code)
    print("Response:", response.text)

By the code print(access_token) I can get the token string so I think I do it right on get a token. But I got 401 response when calling SharePoint API.

Status Code: 401 Response: {"error_description":"ID3035: The request was not valid or is malformed."}

What could be the problem? Thanks for your advice.


Solution

  • I registered one Entra ID application and granted API permissions as below:

    enter image description here

    Initially, I got same error when I ran your python code in my environment like this:

    import requests
    from msal import ConfidentialClientApplication
    
    client_id = "xxxxxxxxxxxxxxxxxxxxx"
    client_secret = "yyyyyyyyyyyyyyyyyyyyy"
    tenant_id = "tttttttttttttttttttttttttttttttttttttttt"
    site_url = "https://{mytenent}.sharepoint.com/sites/mysite"
    resource = "https://{mytenent}"
    
    # Authenticate using client ID and secret
    authority = f"https://login.microsoftonline.com/{tenant_id}"
    app = ConfidentialClientApplication(
        client_id=client_id,
        authority=authority,
        client_credential=client_secret,
    )
    
    scope=[resource + "/.default"]
    token_response = app.acquire_token_for_client(scopes=scope)
    
    
    access_token = token_response.get("access_token") #token_response["access_token"]
    print(access_token)
    
    endpoint_url = f"{site_url}/_api/web"
    headers = {
        "Authorization": "Bearer " + access_token,
        "Accept": "application/json;odata=verbose",
        "Content-Type": "application/json;odata=verbose",
    }
    print(endpoint_url)
    response = requests.get(endpoint_url, headers=headers)
    
    # Check for errors in the SharePoint API response
    if response.status_code == 200:
        data = response.json()
        print("SharePoint Site Title:", data["d"]["Title"])
    else:
        print("SharePoint API Error:")
        print("Status Code:", response.status_code)
        print("Response:", response.text)
    

    Response:

    enter image description here

    To resolve the error, you can switch to delegated flows like interactive flow, authorization code flow etc.. for generating token as you granted permissions of Delegated type.

    In my case, I ran below modified code that uses interactive flow for generating token and got response successfully like this:

    import requests
    import msal
    
    client_id = "appId"
    tenant_id = "tenantId"
    site_url = "https://tenant.sharepoint.com/sites/sridemosite"
    resource = "https://tenant.sharepoint.com"
    
    # Authenticate using client ID and secret
    authority = f"https://login.microsoftonline.com/{tenant_id}"
    
    app = msal.PublicClientApplication(
        client_id,
        authority=authority,
    )
    scope=[resource + "/.default"]
    token_response = app.acquire_token_interactive(scopes=scope)
    access_token = token_response['access_token']
    print(access_token)
    
    endpoint_url = f"{site_url}/_api/web"
    headers = {
        "Authorization": "Bearer " + access_token,
        "Accept": "application/json;odata=verbose",
        "Content-Type": "application/json;odata=verbose",
    }
    print()
    print(endpoint_url)
    response = requests.get(endpoint_url, headers=headers)
    
    # Check for errors in the SharePoint API response
    if response.status_code == 200:
        data = response.json()
        print("SharePoint Site Title:", data["d"]["Title"])
    else:
        print("SharePoint API Error:")
        print("Status Code:", response.status_code)
        print("Response:", response.text)
    

    Response:

    enter image description here

    Make sure to enable public client flow and add redirect URI in Mobile and desktop applications platform while using interactive flow:

    enter image description here

    Alternatively, you can also generate access token using client certificate by granting permissions of Application type, refer below blog:

    Getting an App Only access token for SharePoint REST APIs by Martin Loitzl