microsoft-graph-apiazure-authenticationms-formsdjango-microsoft-authentication

Encountering 403 Error While Trying to Access Microsoft Forms URLs, despite successfully obtaining the authentication token


I'm currently working on a project where I need to access various URLs related to Microsoft Forms from my Python backend. However, I'm facing a 403 error when attempting to do so. Here's a breakdown of what I'm trying to achieve and the steps I've taken so far:

URLs to access: Retrieve all Forms for a Microsoft 365 Group:

https://forms.office.com/formapi/api/{tenantId}/groups/{groupId}/forms

Obtain details for a group form:

https://forms.office.com/formapi/api/{tenantId}/groups/{groupId}/forms('{formId}')

Fetch questions from a group form:

https://forms.office.com/formapi/api/{tenantId}/groups/{groupId}/forms('{formId}')/questions

Retrieve responses to a group form:

https://forms.office.com/formapi/api/{tenantId}/groups/{groupId}/forms('{formId}')/responses

Objective: Access URLs for Microsoft Forms data from backend.

Approach:

First, I log in to the MS Forms website.

Then, I append the necessary parameters (tenantId and groupId) to the URLs I want to access, and I am getting browser responses in JSON format.

Now using backend requests, I attempt to access these URLs by passing the required authentication token.

Issue: Despite successfully obtaining the authentication token using the provided code snippet, when I tried to access the Microsoft Forms URLs with the generated token, I received a 403 error.

Here's the code snippet I'm using to obtain the token:


token_url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token" 

data = { 

    "grant_type": "client_credentials", 

    "client_id": client_id, 

    "client_secret": client_secret, 

    "scope": "https://forms.office.com/.default" 

} 

response = requests.post(token_url, data=data) 

response.raise_for_status()  # Raise an exception for non-200 status codes 

And here's the code snippet I'm using to access the Microsoft Forms URLs with the obtained token:


headers = { 

    'Authorization': f'Bearer {response.json()["access_token"]}', 

    'Content-Type': 'application/json', 

} 

base_url = "https://forms.office.com/formapi/api/{tenantId}/groups/{groupId}/forms" 

list_response = requests.get(url=base_url, headers=headers) 

I'm uncertain why I'm receiving a 403 error despite having a valid token. I followed the instructions mentioned in this post, but was unsuccessful.

Any insights or assistance on resolving this issue would be highly appreciated.

Thank you in advance!


Solution

  • Initially, I too got same error when I used token generated with client credentials flow to call MS Forms API like this:

    import requests
    
    tenant_id = "tenantId"
    client_id = "appID"
    client_secret = "secret"
    
    token_url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
    
    data = {
        "grant_type": "client_credentials",
        "client_id": client_id,
        "client_secret": client_secret,
        "scope": "https://forms.office.com/.default"
    }
    
    response = requests.post(token_url, data=data)
    
    headers = {
        'Authorization': f'Bearer {response.json()["access_token"]}',
        'Content-Type': 'application/json',
    }
    
    base_url = "https://forms.office.com/formapi/api/tenantId/groups/groupId/forms"
    
    list_response = requests.get(url=base_url, headers=headers)
    print(list_response)
    

    Response:

    enter image description here

    To resolve the error, you need to switch to delegated flows for generating bearer token that requires user to login at least once.

    In my case, I ran below CLI command to connect with Azure account initially in terminal like this:

    az login --tenant tenantID
    

    enter image description here

    Now, I ran below sample Python code that uses azure-identity module by generating token with AzureCliCredential() method and got forms details in response successfully:

    import requests
    
    from azure.identity import DefaultAzureCredential, InteractiveBrowserCredential, AzureCliCredential
    
    # Use one of these credential objects to get an access token
    cred = AzureCliCredential() # i.e. `az login`
    # cred = InteractiveBrowserCredential()
    # cred = DefaultAzureCredential()
    
    # Request an access token with the following scope
    scope = "https://forms.office.com/.default"
    
    token = cred.get_token(scope)
    
    tenantId = "tenantId"
    groupId = "groupId"
    
    url = f"https://forms.office.com/formapi/api/{tenantId}/groups/{groupId}/forms"
    
    # Provide the access token in the request header
    headers = {"Authorization": f"Bearer {token.token}"}
    
    list_response = requests.get(url, headers=headers)
    print(list_response.json())
    

    Response:

    enter image description here

    Reference:

    How to have access to an online MS Form responses with code? - Stack Overflow by 9nut